[v2,1/4] net: fec: set GPR bit on suspend by DT configuration.
diff mbox series

Message ID 1585159919-11491-2-git-send-email-martin.fuzzey@flowbird.group
State Changes Requested
Delegated to: David Miller
Headers show
Series
  • Fix Wake on lan with FEC on i.MX6
Related show

Commit Message

Martin Fuzzey March 25, 2020, 6:11 p.m. UTC
On some SoCs, such as the i.MX6, it is necessary to set a bit
in the SoC level GPR register before suspending for wake on lan
to work.

The fec platform callback sleep_mode_enable was intended to allow this
but the platform implementation was NAK'd back in 2015 [1]

This means that, currently, wake on lan is broken on mainline for
the i.MX6 at least.

So implement the required bit setting in the fec driver by itself
by adding a new optional DT property indicating the GPR register
and adding the offset and bit information to the driver.

[1] https://www.spinics.net/lists/netdev/msg310922.html

Signed-off-by: Martin Fuzzey <martin.fuzzey@flowbird.group>
Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
---
 drivers/net/ethernet/freescale/fec.h      |   7 ++
 drivers/net/ethernet/freescale/fec_main.c | 149 ++++++++++++++++++++++++------
 2 files changed, 127 insertions(+), 29 deletions(-)

Comments

Andy Duan March 26, 2020, 7:57 a.m. UTC | #1
From: Martin Fuzzey <martin.fuzzey@flowbird.group> Sent: Thursday, March 26, 2020 2:12 AM
> On some SoCs, such as the i.MX6, it is necessary to set a bit in the SoC level
> GPR register before suspending for wake on lan to work.
> 
> The fec platform callback sleep_mode_enable was intended to allow this but
> the platform implementation was NAK'd back in 2015 [1]
> 
> This means that, currently, wake on lan is broken on mainline for the i.MX6 at
> least.
> 
> So implement the required bit setting in the fec driver by itself by adding a
> new optional DT property indicating the GPR register and adding the offset
> and bit information to the driver.
> 
> [1]
> https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.s
> pinics.net%2Flists%2Fnetdev%2Fmsg310922.html&amp;data=02%7C01%7Cf
> ugang.duan%40nxp.com%7Ce3cf15de6619429eb23108d7d0e8036a%7C686e
> a1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C637207567214747980&am
> p;sdata=jfJGqJg7b0u31qfUDr6nxJPeKgp%2FisoTQSOJ607v6KM%3D&amp;rese
> rved=0
> 
> Signed-off-by: Martin Fuzzey <martin.fuzzey@flowbird.group>
> Signed-off-by: Fugang Duan <fugang.duan@nxp.com>

The version look much better.

> ---
>  drivers/net/ethernet/freescale/fec.h      |   7 ++
>  drivers/net/ethernet/freescale/fec_main.c | 149
> ++++++++++++++++++++++++------
>  2 files changed, 127 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/net/ethernet/freescale/fec.h
> b/drivers/net/ethernet/freescale/fec.h
> index f79e57f..d89568f 100644
> --- a/drivers/net/ethernet/freescale/fec.h
> +++ b/drivers/net/ethernet/freescale/fec.h
> @@ -488,6 +488,12 @@ struct fec_enet_priv_rx_q {
>         struct  sk_buff *rx_skbuff[RX_RING_SIZE];  };
> 
> +struct fec_stop_mode_gpr {
> +       struct regmap *gpr;
> +       u8 reg;
> +       u8 bit;
> +};
> +
>  /* The FEC buffer descriptors track the ring buffers.  The rx_bd_base and
>   * tx_bd_base always point to the base of the buffer descriptors.  The
>   * cur_rx and cur_tx point to the currently available buffer.
> @@ -562,6 +568,7 @@ struct fec_enet_private {
>         int hwts_tx_en;
>         struct delayed_work time_keep;
>         struct regulator *reg_phy;
> +       struct fec_stop_mode_gpr stop_gpr;
> 
>         unsigned int tx_align;
>         unsigned int rx_align;
> diff --git a/drivers/net/ethernet/freescale/fec_main.c
> b/drivers/net/ethernet/freescale/fec_main.c
> index 23c5fef..69cab0b 100644
> --- a/drivers/net/ethernet/freescale/fec_main.c
> +++ b/drivers/net/ethernet/freescale/fec_main.c
> @@ -62,6 +62,8 @@
>  #include <linux/if_vlan.h>
>  #include <linux/pinctrl/consumer.h>
>  #include <linux/prefetch.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
>  #include <soc/imx/cpuidle.h>
> 
>  #include <asm/cacheflush.h>
> @@ -84,6 +86,56 @@
>  #define FEC_ENET_OPD_V 0xFFF0
>  #define FEC_MDIO_PM_TIMEOUT  100 /* ms */
> 
> +struct fec_devinfo {
> +       u32 quirks;
> +       u8 stop_gpr_reg;
> +       u8 stop_gpr_bit;
> +};
> +
> +static const struct fec_devinfo fec_imx25_info = {
> +       .quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR |
> +                 FEC_QUIRK_HAS_FRREG,
> +};
> +
> +static const struct fec_devinfo fec_imx27_info = {
> +       .quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG, };
> +
> +static const struct fec_devinfo fec_imx28_info = {
> +       .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
> +                 FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC |
> +                 FEC_QUIRK_HAS_FRREG,
> +};
> +
> +static const struct fec_devinfo fec_imx6q_info = {
> +       .quirks = 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,
> +       .stop_gpr_reg = 0x34,
> +       .stop_gpr_bit = 27,
> +};
> +
> +static const struct fec_devinfo fec_mvf600_info = {
> +       .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, };
> +
> +static const struct fec_devinfo fec_imx6x_info = {
> +       .quirks = 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, };
> +
> +static const struct fec_devinfo fec_imx6ul_info = {
> +       .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
> +                 FEC_QUIRK_HAS_BUFDESC_EX |
> FEC_QUIRK_HAS_CSUM |
> +                 FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 |
> +                 FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC
> |
> +                 FEC_QUIRK_HAS_COALESCE, };
> +
>  static struct platform_device_id fec_devtype[] = {
>         {
>                 /* keep it for coldfire */ @@ -91,39 +143,25 @@
>                 .driver_data = 0,
>         }, {
>                 .name = "imx25-fec",
> -               .driver_data = FEC_QUIRK_USE_GASKET |
> FEC_QUIRK_MIB_CLEAR |
> -                              FEC_QUIRK_HAS_FRREG,
> +               .driver_data = (kernel_ulong_t)&fec_imx25_info,
>         }, {
>                 .name = "imx27-fec",
> -               .driver_data = FEC_QUIRK_MIB_CLEAR |
> FEC_QUIRK_HAS_FRREG,
> +               .driver_data = (kernel_ulong_t)&fec_imx27_info,
>         }, {
>                 .name = "imx28-fec",
> -               .driver_data = FEC_QUIRK_ENET_MAC |
> FEC_QUIRK_SWAP_FRAME |
> -                               FEC_QUIRK_SINGLE_MDIO |
> FEC_QUIRK_HAS_RACC |
> -                               FEC_QUIRK_HAS_FRREG,
> +               .driver_data = (kernel_ulong_t)&fec_imx28_info,
>         }, {
>                 .name = "imx6q-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_ERR006358 |
> -                               FEC_QUIRK_HAS_RACC,
> +               .driver_data = (kernel_ulong_t)&fec_imx6q_info,
>         }, {
>                 .name = "mvf600-fec",
> -               .driver_data = FEC_QUIRK_ENET_MAC |
> FEC_QUIRK_HAS_RACC,
> +               .driver_data = (kernel_ulong_t)&fec_mvf600_info,
>         }, {
>                 .name = "imx6sx-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,
> +               .driver_data = (kernel_ulong_t)&fec_imx6x_info,
>         }, {
>                 .name = "imx6ul-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_ERR007885 |
> -                               FEC_QUIRK_BUG_CAPTURE |
> FEC_QUIRK_HAS_RACC |
> -                               FEC_QUIRK_HAS_COALESCE,
> +               .driver_data = (kernel_ulong_t)&fec_imx6ul_info,
>         }, {
>                 /* sentinel */
>         }
> @@ -1092,11 +1130,28 @@ static void fec_enet_reset_skb(struct
> net_device *ndev)
> 
>  }
> 
> +static void fec_enet_stop_mode(struct fec_enet_private *fep, bool
> +enabled) {
> +       struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
> +       struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr;
> +
> +       if (stop_gpr->gpr) {
> +               if (enabled)
> +                       regmap_update_bits(stop_gpr->gpr,
> stop_gpr->reg,
> +                                          BIT(stop_gpr->bit),
> +                                          BIT(stop_gpr->bit));
> +               else
> +                       regmap_update_bits(stop_gpr->gpr,
> stop_gpr->reg,
> +                                          BIT(stop_gpr->bit), 0);
> +       } else if (pdata && pdata->sleep_mode_enable) {
> +               pdata->sleep_mode_enable(enabled);
> +       }
> +}
> +
>  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;
> 
> @@ -1125,9 +1180,7 @@ static void fec_enet_reset_skb(struct net_device
> *ndev)
>                 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);
> 
> @@ -3397,6 +3450,37 @@ static int fec_enet_get_irq_cnt(struct
> platform_device *pdev)
>         return irq_cnt;
>  }
> 
> +static int fec_enet_init_stop_mode(struct fec_enet_private *fep,
> +                                  struct fec_devinfo *dev_info,
> +                                  struct device_node *np) {
> +       struct device_node *gpr_np;
> +       int ret;
> +
> +       if (!dev_info)
> +               return 0;
> +
> +       gpr_np = of_parse_phandle(np, "gpr", 0);
> +       if (!gpr_np)
> +               return 0;
> +
> +       fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np);
> +       if (IS_ERR(fep->stop_gpr.gpr)) {
> +               dev_err(&fep->pdev->dev, "could not find gpr regmap\n");
> +               ret = PTR_ERR(fep->stop_gpr.gpr);
> +               fep->stop_gpr.gpr = NULL;
> +               goto out;
> +       }
> +
> +       fep->stop_gpr.reg = dev_info->stop_gpr_reg;
> +       fep->stop_gpr.bit = dev_info->stop_gpr_bit;
> +
> +out:
> +       of_node_put(gpr_np);
> +
> +       return ret;
> +}
> +
>  static int
>  fec_probe(struct platform_device *pdev)  { @@ -3412,6 +3496,7 @@
> static int fec_enet_get_irq_cnt(struct platform_device *pdev)
>         int num_rx_qs;
>         char irq_name[8];
>         int irq_cnt;
> +       struct fec_devinfo *dev_info;
> 
>         fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
> 
> @@ -3429,7 +3514,9 @@ static int fec_enet_get_irq_cnt(struct
> platform_device *pdev)
>         of_id = of_match_device(fec_dt_ids, &pdev->dev);
>         if (of_id)
>                 pdev->id_entry = of_id->data;
> -       fep->quirks = pdev->id_entry->driver_data;
> +       dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data;
> +       if (dev_info)
> +               fep->quirks = dev_info->quirks;
> 
>         fep->netdev = ndev;
>         fep->num_rx_queues = num_rx_qs;
> @@ -3463,6 +3550,10 @@ static int fec_enet_get_irq_cnt(struct
> platform_device *pdev)
>         if (of_get_property(np, "fsl,magic-packet", NULL))
>                 fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
> 
> +       ret = fec_enet_init_stop_mode(fep, dev_info, np);
> +       if (ret)
> +               goto failed_stop_mode;
> +
>         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); @@ -3631,6
> +3722,7 @@ static int fec_enet_get_irq_cnt(struct platform_device *pdev)
>         if (of_phy_is_fixed_link(np))
>                 of_phy_deregister_fixed_link(np);
>         of_node_put(phy_node);
> +failed_stop_mode:
>  failed_phy:
>         dev_id--;
>  failed_ioremap:
> @@ -3708,7 +3800,6 @@ 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 val;
> 
> @@ -3726,8 +3817,8 @@ static int __maybe_unused fec_resume(struct
> device *dev)
>                         goto failed_clk;
>                 }
>                 if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) {
> -                       if (pdata && pdata->sleep_mode_enable)
> -                               pdata->sleep_mode_enable(false);
> +                       fec_enet_stop_mode(fep, false);
> +
>                         val = readl(fep->hwp + FEC_ECNTRL);
>                         val &= ~(FEC_ECR_MAGICEN |
> FEC_ECR_SLEEP);
>                         writel(val, fep->hwp + FEC_ECNTRL);
> --
> 1.9.1

Patch
diff mbox series

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index f79e57f..d89568f 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -488,6 +488,12 @@  struct fec_enet_priv_rx_q {
 	struct  sk_buff *rx_skbuff[RX_RING_SIZE];
 };
 
+struct fec_stop_mode_gpr {
+	struct regmap *gpr;
+	u8 reg;
+	u8 bit;
+};
+
 /* The FEC buffer descriptors track the ring buffers.  The rx_bd_base and
  * tx_bd_base always point to the base of the buffer descriptors.  The
  * cur_rx and cur_tx point to the currently available buffer.
@@ -562,6 +568,7 @@  struct fec_enet_private {
 	int hwts_tx_en;
 	struct delayed_work time_keep;
 	struct regulator *reg_phy;
+	struct fec_stop_mode_gpr stop_gpr;
 
 	unsigned int tx_align;
 	unsigned int rx_align;
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 23c5fef..69cab0b 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -62,6 +62,8 @@ 
 #include <linux/if_vlan.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/prefetch.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 #include <soc/imx/cpuidle.h>
 
 #include <asm/cacheflush.h>
@@ -84,6 +86,56 @@ 
 #define FEC_ENET_OPD_V	0xFFF0
 #define FEC_MDIO_PM_TIMEOUT  100 /* ms */
 
+struct fec_devinfo {
+	u32 quirks;
+	u8 stop_gpr_reg;
+	u8 stop_gpr_bit;
+};
+
+static const struct fec_devinfo fec_imx25_info = {
+	.quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR |
+		  FEC_QUIRK_HAS_FRREG,
+};
+
+static const struct fec_devinfo fec_imx27_info = {
+	.quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG,
+};
+
+static const struct fec_devinfo fec_imx28_info = {
+	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
+		  FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC |
+		  FEC_QUIRK_HAS_FRREG,
+};
+
+static const struct fec_devinfo fec_imx6q_info = {
+	.quirks = 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,
+	.stop_gpr_reg = 0x34,
+	.stop_gpr_bit = 27,
+};
+
+static const struct fec_devinfo fec_mvf600_info = {
+	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC,
+};
+
+static const struct fec_devinfo fec_imx6x_info = {
+	.quirks = 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,
+};
+
+static const struct fec_devinfo fec_imx6ul_info = {
+	.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
+		  FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
+		  FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 |
+		  FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC |
+		  FEC_QUIRK_HAS_COALESCE,
+};
+
 static struct platform_device_id fec_devtype[] = {
 	{
 		/* keep it for coldfire */
@@ -91,39 +143,25 @@ 
 		.driver_data = 0,
 	}, {
 		.name = "imx25-fec",
-		.driver_data = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR |
-			       FEC_QUIRK_HAS_FRREG,
+		.driver_data = (kernel_ulong_t)&fec_imx25_info,
 	}, {
 		.name = "imx27-fec",
-		.driver_data = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG,
+		.driver_data = (kernel_ulong_t)&fec_imx27_info,
 	}, {
 		.name = "imx28-fec",
-		.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
-				FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC |
-				FEC_QUIRK_HAS_FRREG,
+		.driver_data = (kernel_ulong_t)&fec_imx28_info,
 	}, {
 		.name = "imx6q-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_ERR006358 |
-				FEC_QUIRK_HAS_RACC,
+		.driver_data = (kernel_ulong_t)&fec_imx6q_info,
 	}, {
 		.name = "mvf600-fec",
-		.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC,
+		.driver_data = (kernel_ulong_t)&fec_mvf600_info,
 	}, {
 		.name = "imx6sx-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,
+		.driver_data = (kernel_ulong_t)&fec_imx6x_info,
 	}, {
 		.name = "imx6ul-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_ERR007885 |
-				FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC |
-				FEC_QUIRK_HAS_COALESCE,
+		.driver_data = (kernel_ulong_t)&fec_imx6ul_info,
 	}, {
 		/* sentinel */
 	}
@@ -1092,11 +1130,28 @@  static void fec_enet_reset_skb(struct net_device *ndev)
 
 }
 
+static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled)
+{
+	struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
+	struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr;
+
+	if (stop_gpr->gpr) {
+		if (enabled)
+			regmap_update_bits(stop_gpr->gpr, stop_gpr->reg,
+					   BIT(stop_gpr->bit),
+					   BIT(stop_gpr->bit));
+		else
+			regmap_update_bits(stop_gpr->gpr, stop_gpr->reg,
+					   BIT(stop_gpr->bit), 0);
+	} else if (pdata && pdata->sleep_mode_enable) {
+		pdata->sleep_mode_enable(enabled);
+	}
+}
+
 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;
 
@@ -1125,9 +1180,7 @@  static void fec_enet_reset_skb(struct net_device *ndev)
 		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);
 
@@ -3397,6 +3450,37 @@  static int fec_enet_get_irq_cnt(struct platform_device *pdev)
 	return irq_cnt;
 }
 
+static int fec_enet_init_stop_mode(struct fec_enet_private *fep,
+				   struct fec_devinfo *dev_info,
+				   struct device_node *np)
+{
+	struct device_node *gpr_np;
+	int ret;
+
+	if (!dev_info)
+		return 0;
+
+	gpr_np = of_parse_phandle(np, "gpr", 0);
+	if (!gpr_np)
+		return 0;
+
+	fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np);
+	if (IS_ERR(fep->stop_gpr.gpr)) {
+		dev_err(&fep->pdev->dev, "could not find gpr regmap\n");
+		ret = PTR_ERR(fep->stop_gpr.gpr);
+		fep->stop_gpr.gpr = NULL;
+		goto out;
+	}
+
+	fep->stop_gpr.reg = dev_info->stop_gpr_reg;
+	fep->stop_gpr.bit = dev_info->stop_gpr_bit;
+
+out:
+	of_node_put(gpr_np);
+
+	return ret;
+}
+
 static int
 fec_probe(struct platform_device *pdev)
 {
@@ -3412,6 +3496,7 @@  static int fec_enet_get_irq_cnt(struct platform_device *pdev)
 	int num_rx_qs;
 	char irq_name[8];
 	int irq_cnt;
+	struct fec_devinfo *dev_info;
 
 	fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
 
@@ -3429,7 +3514,9 @@  static int fec_enet_get_irq_cnt(struct platform_device *pdev)
 	of_id = of_match_device(fec_dt_ids, &pdev->dev);
 	if (of_id)
 		pdev->id_entry = of_id->data;
-	fep->quirks = pdev->id_entry->driver_data;
+	dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data;
+	if (dev_info)
+		fep->quirks = dev_info->quirks;
 
 	fep->netdev = ndev;
 	fep->num_rx_queues = num_rx_qs;
@@ -3463,6 +3550,10 @@  static int fec_enet_get_irq_cnt(struct platform_device *pdev)
 	if (of_get_property(np, "fsl,magic-packet", NULL))
 		fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
 
+	ret = fec_enet_init_stop_mode(fep, dev_info, np);
+	if (ret)
+		goto failed_stop_mode;
+
 	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);
@@ -3631,6 +3722,7 @@  static int fec_enet_get_irq_cnt(struct platform_device *pdev)
 	if (of_phy_is_fixed_link(np))
 		of_phy_deregister_fixed_link(np);
 	of_node_put(phy_node);
+failed_stop_mode:
 failed_phy:
 	dev_id--;
 failed_ioremap:
@@ -3708,7 +3800,6 @@  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 val;
 
@@ -3726,8 +3817,8 @@  static int __maybe_unused fec_resume(struct device *dev)
 			goto failed_clk;
 		}
 		if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) {
-			if (pdata && pdata->sleep_mode_enable)
-				pdata->sleep_mode_enable(false);
+			fec_enet_stop_mode(fep, false);
+
 			val = readl(fep->hwp + FEC_ECNTRL);
 			val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
 			writel(val, fep->hwp + FEC_ECNTRL);