diff mbox series

net: fec: Refactor MAC reset to function

Message ID 20250121103857.12007-3-csokas.bence@prolan.hu (mailing list archive)
State New
Headers show
Series net: fec: Refactor MAC reset to function | expand

Commit Message

Csókás Bence Jan. 21, 2025, 10:38 a.m. UTC
The core is reset both in `fec_restart()`
(called on link-up) and `fec_stop()`
(going to sleep, driver remove etc.).
These two functions had their separate
implementations, which was at first only
a register write and a `udelay()` (and
the accompanying block comment).
However, since then we got soft-reset
(MAC disable) and Wake-on-LAN support,
which meant that these implementations
diverged, often causing bugs. For instance,
as of now, `fec_stop()` does not check for
`FEC_QUIRK_NO_HARD_RESET`. To eliminate
this bug-source, refactor implementation
to a common function.

Fixes: c730ab423bfa ("net: fec: Fix temporary RMII clock reset on link up")
Signed-off-by: Csókás, Bence <csokas.bence@prolan.hu>
---

Notes:
    Recommended options for this patch:
    `--color-moved --color-moved-ws=allow-indentation-change`

 drivers/net/ethernet/freescale/fec_main.c | 50 +++++++++++------------
 1 file changed, 23 insertions(+), 27 deletions(-)

Comments

Michal Swiatkowski Jan. 21, 2025, 2:10 p.m. UTC | #1
On Tue, Jan 21, 2025 at 11:38:58AM +0100, Csókás, Bence wrote:
> The core is reset both in `fec_restart()`
> (called on link-up) and `fec_stop()`
> (going to sleep, driver remove etc.).
> These two functions had their separate
> implementations, which was at first only
> a register write and a `udelay()` (and
> the accompanying block comment).
> However, since then we got soft-reset
> (MAC disable) and Wake-on-LAN support,
> which meant that these implementations
> diverged, often causing bugs. For instance,
> as of now, `fec_stop()` does not check for
> `FEC_QUIRK_NO_HARD_RESET`. To eliminate
> this bug-source, refactor implementation
> to a common function.
> 
> Fixes: c730ab423bfa ("net: fec: Fix temporary RMII clock reset on link up")
> Signed-off-by: Csókás, Bence <csokas.bence@prolan.hu>
> ---
> 
> Notes:
>     Recommended options for this patch:
>     `--color-moved --color-moved-ws=allow-indentation-change`
> 
>  drivers/net/ethernet/freescale/fec_main.c | 50 +++++++++++------------
>  1 file changed, 23 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
> index 68725506a095..850ef3de74ec 100644
> --- a/drivers/net/ethernet/freescale/fec_main.c
> +++ b/drivers/net/ethernet/freescale/fec_main.c
> @@ -1064,6 +1064,27 @@ static void fec_enet_enable_ring(struct net_device *ndev)
>  	}
>  }
>  
> +/* Whack a reset.  We should wait for this.
> + * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> + * instead of reset MAC itself.
> + */
> +static void fec_ctrl_reset(struct fec_enet_private *fep, bool wol)
> +{
> +	if (!wol || !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
> +		if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
> +		    ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
> +			writel(0, fep->hwp + FEC_ECNTRL);
> +		} else {
> +			writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
> +			udelay(10);
> +		}
> +	} else {
> +		val = readl(fep->hwp + FEC_ECNTRL);
> +		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
> +		writel(val, fep->hwp + FEC_ECNTRL);
> +	}
> +}
> +
>  /*
>   * This function is called to start or restart the FEC during a link
>   * change, transmit timeout, or to reconfigure the FEC.  The network
> @@ -1080,17 +1101,7 @@ fec_restart(struct net_device *ndev)
>  	if (fep->bufdesc_ex)
>  		fec_ptp_save_state(fep);
>  
> -	/* Whack a reset.  We should wait for this.
> -	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> -	 * instead of reset MAC itself.
> -	 */
> -	if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
> -	    ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
> -		writel(0, fep->hwp + FEC_ECNTRL);
> -	} else {
> -		writel(1, fep->hwp + FEC_ECNTRL);
Nit, in case of a need for v2 you can mark in commit message that
FEC_ECR_RESET == 1, so define can be use instead of 1.

> -		udelay(10);
> -	}
> +	fec_ctrl_reset(fep, false);
>  
>  	/*
>  	 * enet-mac reset will reset mac address registers too,
> @@ -1344,22 +1355,7 @@ fec_stop(struct net_device *ndev)
>  	if (fep->bufdesc_ex)
>  		fec_ptp_save_state(fep);
>  
> -	/* Whack a reset.  We should wait for this.
> -	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> -	 * instead of reset MAC itself.
> -	 */
> -	if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
> -		if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) {
> -			writel(0, fep->hwp + FEC_ECNTRL);
> -		} else {
> -			writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
> -			udelay(10);
> -		}
> -	} else {
> -		val = readl(fep->hwp + FEC_ECNTRL);
> -		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
> -		writel(val, fep->hwp + FEC_ECNTRL);
> -	}
> +	fec_ctrl_reset(fep, true);
>  	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
>  	writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
>  

Reviewed-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>

> -- 
> 2.48.1
Ahmad Fatoum Jan. 21, 2025, 2:36 p.m. UTC | #2
Hi,

On 21.01.25 11:38, Csókás, Bence wrote:
> The core is reset both in `fec_restart()`
> (called on link-up) and `fec_stop()`
> (going to sleep, driver remove etc.).
> These two functions had their separate
> implementations, which was at first only
> a register write and a `udelay()` (and
> the accompanying block comment).
> However, since then we got soft-reset
> (MAC disable) and Wake-on-LAN support,
> which meant that these implementations
> diverged, often causing bugs. For instance,
> as of now, `fec_stop()` does not check for
> `FEC_QUIRK_NO_HARD_RESET`. To eliminate
> this bug-source, refactor implementation
> to a common function.

please make the lines a bit longer for v2. 43 characters is much too limited.

Thanks,
Ahmad

> 
> Fixes: c730ab423bfa ("net: fec: Fix temporary RMII clock reset on link up")
> Signed-off-by: Csókás, Bence <csokas.bence@prolan.hu>
> ---
> 
> Notes:
>     Recommended options for this patch:
>     `--color-moved --color-moved-ws=allow-indentation-change`
> 
>  drivers/net/ethernet/freescale/fec_main.c | 50 +++++++++++------------
>  1 file changed, 23 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
> index 68725506a095..850ef3de74ec 100644
> --- a/drivers/net/ethernet/freescale/fec_main.c
> +++ b/drivers/net/ethernet/freescale/fec_main.c
> @@ -1064,6 +1064,27 @@ static void fec_enet_enable_ring(struct net_device *ndev)
>  	}
>  }
>  
> +/* Whack a reset.  We should wait for this.
> + * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> + * instead of reset MAC itself.
> + */
> +static void fec_ctrl_reset(struct fec_enet_private *fep, bool wol)
> +{
> +	if (!wol || !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
> +		if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
> +		    ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
> +			writel(0, fep->hwp + FEC_ECNTRL);
> +		} else {
> +			writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
> +			udelay(10);
> +		}
> +	} else {
> +		val = readl(fep->hwp + FEC_ECNTRL);
> +		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
> +		writel(val, fep->hwp + FEC_ECNTRL);
> +	}
> +}
> +
>  /*
>   * This function is called to start or restart the FEC during a link
>   * change, transmit timeout, or to reconfigure the FEC.  The network
> @@ -1080,17 +1101,7 @@ fec_restart(struct net_device *ndev)
>  	if (fep->bufdesc_ex)
>  		fec_ptp_save_state(fep);
>  
> -	/* Whack a reset.  We should wait for this.
> -	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> -	 * instead of reset MAC itself.
> -	 */
> -	if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
> -	    ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
> -		writel(0, fep->hwp + FEC_ECNTRL);
> -	} else {
> -		writel(1, fep->hwp + FEC_ECNTRL);
> -		udelay(10);
> -	}
> +	fec_ctrl_reset(fep, false);
>  
>  	/*
>  	 * enet-mac reset will reset mac address registers too,
> @@ -1344,22 +1355,7 @@ fec_stop(struct net_device *ndev)
>  	if (fep->bufdesc_ex)
>  		fec_ptp_save_state(fep);
>  
> -	/* Whack a reset.  We should wait for this.
> -	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> -	 * instead of reset MAC itself.
> -	 */
> -	if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
> -		if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) {
> -			writel(0, fep->hwp + FEC_ECNTRL);
> -		} else {
> -			writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
> -			udelay(10);
> -		}
> -	} else {
> -		val = readl(fep->hwp + FEC_ECNTRL);
> -		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
> -		writel(val, fep->hwp + FEC_ECNTRL);
> -	}
> +	fec_ctrl_reset(fep, true);
>  	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
>  	writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
>
Simon Horman Jan. 21, 2025, 3:19 p.m. UTC | #3
On Tue, Jan 21, 2025 at 11:38:58AM +0100, Csókás, Bence wrote:
> The core is reset both in `fec_restart()`
> (called on link-up) and `fec_stop()`
> (going to sleep, driver remove etc.).
> These two functions had their separate
> implementations, which was at first only
> a register write and a `udelay()` (and
> the accompanying block comment).
> However, since then we got soft-reset
> (MAC disable) and Wake-on-LAN support,
> which meant that these implementations
> diverged, often causing bugs. For instance,
> as of now, `fec_stop()` does not check for
> `FEC_QUIRK_NO_HARD_RESET`. To eliminate
> this bug-source, refactor implementation
> to a common function.
> 
> Fixes: c730ab423bfa ("net: fec: Fix temporary RMII clock reset on link up")
> Signed-off-by: Csókás, Bence <csokas.bence@prolan.hu>
> ---
> 
> Notes:
>     Recommended options for this patch:
>     `--color-moved --color-moved-ws=allow-indentation-change`
> 
>  drivers/net/ethernet/freescale/fec_main.c | 50 +++++++++++------------
>  1 file changed, 23 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
> index 68725506a095..850ef3de74ec 100644
> --- a/drivers/net/ethernet/freescale/fec_main.c
> +++ b/drivers/net/ethernet/freescale/fec_main.c
> @@ -1064,6 +1064,27 @@ static void fec_enet_enable_ring(struct net_device *ndev)
>  	}
>  }
>  
> +/* Whack a reset.  We should wait for this.
> + * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> + * instead of reset MAC itself.
> + */
> +static void fec_ctrl_reset(struct fec_enet_private *fep, bool wol)
> +{
> +	if (!wol || !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
> +		if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
> +		    ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
> +			writel(0, fep->hwp + FEC_ECNTRL);
> +		} else {
> +			writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
> +			udelay(10);
> +		}
> +	} else {
> +		val = readl(fep->hwp + FEC_ECNTRL);
> +		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
> +		writel(val, fep->hwp + FEC_ECNTRL);

Hi Bence,

It seems that this does not compile because val is not declared in this scope.

> +	}
> +}
> +
>  /*
>   * This function is called to start or restart the FEC during a link
>   * change, transmit timeout, or to reconfigure the FEC.  The network

Please observe the rule regarding not posting updated patches within 24h
when preparing v2.
Csókás Bence Jan. 21, 2025, 3:51 p.m. UTC | #4
Hi all,

On 2025. 01. 21. 16:19, Simon Horman wrote:
> It seems that this does not compile because val is not declared in this scope.

Ohh, shoot, I forgot a `-a` in `git commit --amend`... Rookie mistake; 
will be fixed.

> Please observe the rule regarding not posting updated patches within 24h
> when preparing v2.

Of course.

On 2025. 01. 21. 15:36, Ahmad Fatoum wrote:
 > please make the lines a bit longer for v2. 43 characters is much too 
limited.

Reformatted to 80 cols.

On 2025. 01. 21. 15:10, Michal Swiatkowski wrote:
 >> -	} else {
 >> -		writel(1, fep->hwp + FEC_ECNTRL);
 > Nit, in case of a need for v2 you can mark in commit message that
 > FEC_ECR_RESET == 1, so define can be use instead of 1.

This was somehow missed from my earlier refactor in commit ff049886671c 
("net: fec: Refactor: #define magic constants"). Adding this to the msg.

Bence
Badel, Laurent Jan. 21, 2025, 4:09 p.m. UTC | #5
Hi Bence and thanks for the patch.

Leaving out the check for FEC_QUIRK_NO_HARD_RESET in fec_stop() was, in fact,
not unintentional. Although a hard reset in fec_restart() caused link issues
with the iMX28, I had no particular reason to believe that it would also cause
issues in fec_stop(), since at this point you're turning off the interface, and
I did not observe any particular problems either, so I did not think the same
modification was warranted there.

If you have reason to believe that this is a bug, then it should be fixed, but
currently I don't see why this is the case here. I think a refactoring 
duplicated code is a good idea, but since it also includes a modification of 
the behavior (specifically, there is a possible path where 
FEC_QUIRK_NO_HARD_RESET is set and the link is up, where fec_stop() will issue
a soft reset instead of a hard reset), I would prefer to know that this change
is indeed necessary. 

If others disagree and there's a consensus that this change is ok, I'm happy 
for the patch to get through, but I tend to err on the side of caution in such
cases.

An additional comment - this is just my personal opinion - but in
fec_ctrl_reset(), it seems to me that the function of the wol argument really
is to distinguish if we're using the fec_restart() or the fec_stop()
implementation, so I think the naming may be a bit misleading in this case.

Best regards,

Laurent

On Tue, Jan 21, 2025 at 11:38:58AM +0100, Csókás, Bence wrote:
> The core is reset both in `fec_restart()` (called on link-up) and 
> `fec_stop()` (going to sleep, driver remove etc.).
> These two functions had their separate implementations, which was at 
> first only a register write and a `udelay()` (and the accompanying 
> block comment).
> However, since then we got soft-reset
> (MAC disable) and Wake-on-LAN support, which meant that these 
> implementations diverged, often causing bugs. For instance, as of now, 
> `fec_stop()` does not check for `FEC_QUIRK_NO_HARD_RESET`. To 
> eliminate this bug-source, refactor implementation to a common 
> function.
>
> Fixes: c730ab423bfa ("net: fec: Fix temporary RMII clock reset on link 
> up")
> Signed-off-by: Csókás, Bence <csokas.bence@prolan.hu>
> ---
>
> Notes:
>     Recommended options for this patch:
>     `--color-moved --color-moved-ws=allow-indentation-change`
>
>  drivers/net/ethernet/freescale/fec_main.c | 50 
> +++++++++++------------
>  1 file changed, 23 insertions(+), 27 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/fec_main.c 
> b/drivers/net/ethernet/freescale/fec_main.c
> index 68725506a095..850ef3de74ec 100644
> --- a/drivers/net/ethernet/freescale/fec_main.c
> +++ b/drivers/net/ethernet/freescale/fec_main.c
> @@ -1064,6 +1064,27 @@ static void fec_enet_enable_ring(struct net_device *ndev)
>       }
>  }
>
> +/* Whack a reset.  We should wait for this.
> + * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> + * instead of reset MAC itself.
> + */
> +static void fec_ctrl_reset(struct fec_enet_private *fep, bool wol) {
> +     if (!wol || !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
> +             if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
> +                 ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
> +                     writel(0, fep->hwp + FEC_ECNTRL);
> +             } else {
> +                     writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
> +                     udelay(10);
> +             }
> +     } else {
> +             val = readl(fep->hwp + FEC_ECNTRL);
> +             val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
> +             writel(val, fep->hwp + FEC_ECNTRL);
> +     }
> +}
> +
>  /*
>   * This function is called to start or restart the FEC during a link
>   * change, transmit timeout, or to reconfigure the FEC.  The network 
> @@ -1080,17 +1101,7 @@ fec_restart(struct net_device *ndev)
>       if (fep->bufdesc_ex)
>               fec_ptp_save_state(fep);
>
> -     /* Whack a reset.  We should wait for this.
> -      * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> -      * instead of reset MAC itself.
> -      */
> -     if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
> -         ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
> -             writel(0, fep->hwp + FEC_ECNTRL);
> -     } else {
> -             writel(1, fep->hwp + FEC_ECNTRL);
Nit, in case of a need for v2 you can mark in commit message that FEC_ECR_RESET == 1, so define can be use instead of 1.

> -             udelay(10);
> -     }
> +     fec_ctrl_reset(fep, false);
>
>       /*
>        * enet-mac reset will reset mac address registers too, @@ 
> -1344,22 +1355,7 @@ fec_stop(struct net_device *ndev)
>       if (fep->bufdesc_ex)
>               fec_ptp_save_state(fep);
>
> -     /* Whack a reset.  We should wait for this.
> -      * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> -      * instead of reset MAC itself.
> -      */
> -     if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
> -             if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) {
> -                     writel(0, fep->hwp + FEC_ECNTRL);
> -             } else {
> -                     writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
> -                     udelay(10);
> -             }
> -     } else {
> -             val = readl(fep->hwp + FEC_ECNTRL);
> -             val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
> -             writel(val, fep->hwp + FEC_ECNTRL);
> -     }
> +     fec_ctrl_reset(fep, true);
>       writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
>       writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
>
> --
> 2.48.1
Andrew Lunn Jan. 21, 2025, 5:28 p.m. UTC | #6
On Tue, Jan 21, 2025 at 11:38:58AM +0100, Csókás, Bence wrote:
> The core is reset both in `fec_restart()`
> (called on link-up) and `fec_stop()`
> (going to sleep, driver remove etc.).
> These two functions had their separate
> implementations, which was at first only
> a register write and a `udelay()` (and
> the accompanying block comment).
> However, since then we got soft-reset
> (MAC disable) and Wake-on-LAN support,
> which meant that these implementations
> diverged, often causing bugs. For instance,
> as of now, `fec_stop()` does not check for
> `FEC_QUIRK_NO_HARD_RESET`. To eliminate
> this bug-source, refactor implementation
> to a common function.
> 
> Fixes: c730ab423bfa ("net: fec: Fix temporary RMII clock reset on link up")
> Signed-off-by: Csókás, Bence <csokas.bence@prolan.hu>

The subject say "Refactor...." A refactor generally does not need a
Fixes: tag. Maybe make the subject reflect the bug you are fixing, and
reword the commit message to focus on the bug being fixed, not the
refactor.

	Andrew
Jakub Kicinski Jan. 21, 2025, 6:07 p.m. UTC | #7
On Tue, 21 Jan 2025 16:51:51 +0100 Csókás Bence wrote:
> On 2025. 01. 21. 15:36, Ahmad Fatoum wrote:
>  > please make the lines a bit longer for v2. 43 characters is much too   
> limited.
> 
> Reformatted to 80 cols.

Quoting "Submitting patches":

  The body of the explanation, line wrapped at 75 columns, which will be
  copied to the permanent changelog to describe this patch.

https://docs.kernel.org/process/submitting-patches.html#the-canonical-patch-format
Csókás Bence Jan. 21, 2025, 8:41 p.m. UTC | #8
Hi Laurent,

On 2025. 01. 21. 17:09, Badel, Laurent wrote:
> Hi Bence and thanks for the patch.

thanks for your input.

> Leaving out the check for FEC_QUIRK_NO_HARD_RESET in fec_stop() was, in fact,
> not unintentional. Although a hard reset in fec_restart() caused link issues
> with the iMX28, I had no particular reason to believe that it would also cause
> issues in fec_stop(), since at this point you're turning off the interface, and
> I did not observe any particular problems either, so I did not think the same
> modification was warranted there.

I had a feeling it was intentional, however, `fec_stop()` is called all 
over the place - not just when removing the interface (e.g. unloading 
the driver), but also by the PM subsystem for entering suspend, 
restarting auto-negotiation, for handling Pause frames and changing 
HW-accelerated RX checksum checking...

> If you have reason to believe that this is a bug, then it should be fixed, but
> currently I don't see why this is the case here. I think a refactoring
> duplicated code is a good idea, but since it also includes a modification of
> the behavior (specifically, there is a possible path where
> FEC_QUIRK_NO_HARD_RESET is set and the link is up, where fec_stop() will issue
> a soft reset instead of a hard reset), I would prefer to know that this change
> is indeed necessary.
> 
> If others disagree and there's a consensus that this change is ok, I'm happy
> for the patch to get through, but I tend to err on the side of caution in such
> cases.

To me, the name `FEC_QUIRK_NO_HARD_RESET`, and its doc-comment seems to 
suggest that we do *not* want to hard-reset this MAC *ever*; not in the 
codepath of `fec_restart()` and not in `fec_stop()`. Did you observe 
problems on i.MX28 if you soft-reset it in stop()? I _might_ be able to 
get my hands on an i.MX287 and test, but I have no idea if it is 
working; I took it out from the junk bin.

Right now, we're chasing a different bug on the i.MX6, and this was just 
meant to reduce the amount of clutter we have to cut through.

> An additional comment - this is just my personal opinion - but in
> fec_ctrl_reset(), it seems to me that the function of the wol argument really
> is to distinguish if we're using the fec_restart() or the fec_stop()
> implementation, so I think the naming may be a bit misleading in this case.

True, but I would prefer to keep it separate, i.e. the `wol` parameter 
should really only control whether we want to enable WoL. If we decide 
to keep the old behavior of not honoring FEC_QUIRK_NO_HARD_RESET in 
stop(), I'd rather add a new parameter `allow_soft_reset`. That way, if 
ever someone needs to call `fec_ctrl_reset()`, they will be able to give 
it values they make sense at that point-of-call, instead of having to 
"fake" either restart() or stop().

Bence
Wei Fang Jan. 22, 2025, 2:50 a.m. UTC | #9
> The core is reset both in `fec_restart()`
> (called on link-up) and `fec_stop()`
> (going to sleep, driver remove etc.).
> These two functions had their separate
> implementations, which was at first only
> a register write and a `udelay()` (and
> the accompanying block comment).
> However, since then we got soft-reset
> (MAC disable) and Wake-on-LAN support,
> which meant that these implementations
> diverged, often causing bugs. For instance,
> as of now, `fec_stop()` does not check for
> `FEC_QUIRK_NO_HARD_RESET`. To eliminate
> this bug-source, refactor implementation
> to a common function.
> 
> Fixes: c730ab423bfa ("net: fec: Fix temporary RMII clock reset on link up")
> Signed-off-by: Csókás, Bence <csokas.bence@prolan.hu>
> ---
> 
> Notes:
>     Recommended options for this patch:
>     `--color-moved --color-moved-ws=allow-indentation-change`
> 
>  drivers/net/ethernet/freescale/fec_main.c | 50 +++++++++++------------
>  1 file changed, 23 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/net/ethernet/freescale/fec_main.c
> b/drivers/net/ethernet/freescale/fec_main.c
> index 68725506a095..850ef3de74ec 100644
> --- a/drivers/net/ethernet/freescale/fec_main.c
> +++ b/drivers/net/ethernet/freescale/fec_main.c
> @@ -1064,6 +1064,27 @@ static void fec_enet_enable_ring(struct net_device
> *ndev)
>  	}
>  }
> 
> +/* Whack a reset.  We should wait for this.
> + * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> + * instead of reset MAC itself.
> + */
> +static void fec_ctrl_reset(struct fec_enet_private *fep, bool wol)

'wol' gives me the feeling that it indicates whether the WOL function
is enabled. And the else branch will be taken if 'wol' is true. But in fact,
even if 'wol' is true, it may go to the reset branch. This is a bit confusing.

How about the following changes?

static void fec_ctrl_reset(struct fec_enet_private *fep, bool wol)
{
	if (wol) {
		u32 val;

		val = readl(fep->hwp + FEC_ECNTRL);
		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
		writel(val, fep->hwp + FEC_ECNTRL);
	} else if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
		((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
		writel(0, fep->hwp + FEC_ECNTRL);
	} else {
		writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
		udelay(10);
	}
}

> +{
> +	if (!wol || !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
> +		if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
> +		    ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
> +			writel(0, fep->hwp + FEC_ECNTRL);
> +		} else {
> +			writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
> +			udelay(10);
> +		}
> +	} else {
> +		val = readl(fep->hwp + FEC_ECNTRL);
> +		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
> +		writel(val, fep->hwp + FEC_ECNTRL);
> +	}
> +}
> +
>  /*
>   * This function is called to start or restart the FEC during a link
>   * change, transmit timeout, or to reconfigure the FEC.  The network
> @@ -1080,17 +1101,7 @@ fec_restart(struct net_device *ndev)
>  	if (fep->bufdesc_ex)
>  		fec_ptp_save_state(fep);
> 
> -	/* Whack a reset.  We should wait for this.
> -	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> -	 * instead of reset MAC itself.
> -	 */
> -	if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
> -	    ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
> -		writel(0, fep->hwp + FEC_ECNTRL);
> -	} else {
> -		writel(1, fep->hwp + FEC_ECNTRL);
> -		udelay(10);
> -	}
> +	fec_ctrl_reset(fep, false);
> 
>  	/*
>  	 * enet-mac reset will reset mac address registers too,
> @@ -1344,22 +1355,7 @@ fec_stop(struct net_device *ndev)
>  	if (fep->bufdesc_ex)
>  		fec_ptp_save_state(fep);
> 
> -	/* Whack a reset.  We should wait for this.
> -	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
> -	 * instead of reset MAC itself.
> -	 */
> -	if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
> -		if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) {
> -			writel(0, fep->hwp + FEC_ECNTRL);
> -		} else {
> -			writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
> -			udelay(10);
> -		}
> -	} else {
> -		val = readl(fep->hwp + FEC_ECNTRL);
> -		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
> -		writel(val, fep->hwp + FEC_ECNTRL);
> -	}
> +	fec_ctrl_reset(fep, true);
>  	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
>  	writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
> 
> --
> 2.48.1
>
diff mbox series

Patch

diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 68725506a095..850ef3de74ec 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -1064,6 +1064,27 @@  static void fec_enet_enable_ring(struct net_device *ndev)
 	}
 }
 
+/* Whack a reset.  We should wait for this.
+ * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
+ * instead of reset MAC itself.
+ */
+static void fec_ctrl_reset(struct fec_enet_private *fep, bool wol)
+{
+	if (!wol || !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
+		if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
+		    ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
+			writel(0, fep->hwp + FEC_ECNTRL);
+		} else {
+			writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
+			udelay(10);
+		}
+	} else {
+		val = readl(fep->hwp + FEC_ECNTRL);
+		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
+		writel(val, fep->hwp + FEC_ECNTRL);
+	}
+}
+
 /*
  * This function is called to start or restart the FEC during a link
  * change, transmit timeout, or to reconfigure the FEC.  The network
@@ -1080,17 +1101,7 @@  fec_restart(struct net_device *ndev)
 	if (fep->bufdesc_ex)
 		fec_ptp_save_state(fep);
 
-	/* Whack a reset.  We should wait for this.
-	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
-	 * instead of reset MAC itself.
-	 */
-	if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES ||
-	    ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
-		writel(0, fep->hwp + FEC_ECNTRL);
-	} else {
-		writel(1, fep->hwp + FEC_ECNTRL);
-		udelay(10);
-	}
+	fec_ctrl_reset(fep, false);
 
 	/*
 	 * enet-mac reset will reset mac address registers too,
@@ -1344,22 +1355,7 @@  fec_stop(struct net_device *ndev)
 	if (fep->bufdesc_ex)
 		fec_ptp_save_state(fep);
 
-	/* Whack a reset.  We should wait for this.
-	 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
-	 * instead of reset MAC itself.
-	 */
-	if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
-		if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) {
-			writel(0, fep->hwp + FEC_ECNTRL);
-		} else {
-			writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL);
-			udelay(10);
-		}
-	} else {
-		val = readl(fep->hwp + FEC_ECNTRL);
-		val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
-		writel(val, fep->hwp + FEC_ECNTRL);
-	}
+	fec_ctrl_reset(fep, true);
 	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
 	writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);