diff mbox series

[net-next,1/6] net: dsa: vsc73xx: convert to PHYLINK

Message ID 20230621191302.1405623-1-paweldembicki@gmail.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series [net-next,1/6] net: dsa: vsc73xx: convert to PHYLINK | expand

Checks

Context Check Description
netdev/series_format warning Series does not have a cover letter
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 8 this patch: 8
netdev/cc_maintainers success CCed 9 of 9 maintainers
netdev/build_clang success Errors and warnings before: 8 this patch: 8
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch warning WARNING: msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.rst
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Pawel Dembicki June 21, 2023, 7:12 p.m. UTC
This patch replaces the adjust_link api with the phylink apis that provide
equivalent functionality.

The remaining functionality from the adjust_link is now covered in the
phylink_mac_link_* and phylink_mac_config.

Removes:
.adjust_link
Adds:
.phylink_get_caps
.phylink_mac_link_down
.phylink_mac_link_up
.phylink_mac_link_down

Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
---
 drivers/net/dsa/vitesse-vsc73xx-core.c | 179 ++++++++++++++-----------
 1 file changed, 99 insertions(+), 80 deletions(-)

Comments

Linus Walleij June 21, 2023, 9:21 p.m. UTC | #1
On Wed, Jun 21, 2023 at 9:13 PM Pawel Dembicki <paweldembicki@gmail.com> wrote:

> This patch replaces the adjust_link api with the phylink apis that provide
> equivalent functionality.
>
> The remaining functionality from the adjust_link is now covered in the
> phylink_mac_link_* and phylink_mac_config.
>
> Removes:
> .adjust_link
> Adds:
> .phylink_get_caps
> .phylink_mac_link_down
> .phylink_mac_link_up
> .phylink_mac_link_down
>
> Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>

Thanks for doing this!
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>

Yours,
Linus Walleij
Simon Horman June 22, 2023, 7:34 a.m. UTC | #2
On Wed, Jun 21, 2023 at 09:12:56PM +0200, Pawel Dembicki wrote:
> This patch replaces the adjust_link api with the phylink apis that provide
> equivalent functionality.
> 
> The remaining functionality from the adjust_link is now covered in the
> phylink_mac_link_* and phylink_mac_config.
> 
> Removes:
> .adjust_link
> Adds:
> .phylink_get_caps
> .phylink_mac_link_down
> .phylink_mac_link_up
> .phylink_mac_link_down
> 
> Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>

...

> +static void vsc73xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
> +					unsigned int mode,
> +					phy_interface_t interface,
> +					struct phy_device *phydev,
> +					int speed, int duplex,
> +					bool tx_pause, bool rx_pause)
> +{
> +	struct vsc73xx *vsc = ds->priv;
> +	u32 val;
>  
> +	switch (speed) {
> +	case SPEED_1000:
>  		/* Set up default for internal port or external RGMII */
> -		if (phydev->interface == PHY_INTERFACE_MODE_RGMII)
> +		if (interface == PHY_INTERFACE_MODE_RGMII)
>  			val = VSC73XX_MAC_CFG_1000M_F_RGMII;
>  		else
>  			val = VSC73XX_MAC_CFG_1000M_F_PHY;
> -		vsc73xx_adjust_enable_port(vsc, port, phydev, val);
> -	} else if (phydev->speed == SPEED_100) {
> -		if (phydev->duplex == DUPLEX_FULL) {
> -			val = VSC73XX_MAC_CFG_100_10M_F_PHY;
> -			dev_dbg(vsc->dev,
> -				"port %d: 100 Mbit full duplex mode\n",
> -				port);
> -		} else {
> -			val = VSC73XX_MAC_CFG_100_10M_H_PHY;
> -			dev_dbg(vsc->dev,
> -				"port %d: 100 Mbit half duplex mode\n",
> -				port);
> -		}
> -		vsc73xx_adjust_enable_port(vsc, port, phydev, val);
> -	} else if (phydev->speed == SPEED_10) {
> -		if (phydev->duplex == DUPLEX_FULL) {
> +		break;
> +	case SPEED_100:
> +	case SPEED_10:
> +		if (duplex == DUPLEX_FULL)
>  			val = VSC73XX_MAC_CFG_100_10M_F_PHY;
> -			dev_dbg(vsc->dev,
> -				"port %d: 10 Mbit full duplex mode\n",
> -				port);
> -		} else {
> +		else
>  			val = VSC73XX_MAC_CFG_100_10M_H_PHY;
> -			dev_dbg(vsc->dev,
> -				"port %d: 10 Mbit half duplex mode\n",
> -				port);
> -		}
> -		vsc73xx_adjust_enable_port(vsc, port, phydev, val);
> -	} else {
> -		dev_err(vsc->dev,
> -			"could not adjust link: unknown speed\n");
> +		break;
>  	}
>  
>  	/* Enable port (forwarding) in the receieve mask */
>  	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
>  			    VSC73XX_RECVMASK, BIT(port), BIT(port));
> +	vsc73xx_adjust_enable_port(vsc, port, val);

Hi Pawel,

GCC 12.3.0 [1] reports that val may now be uninitialised at this point,
and in turn used uninitialised in vsc73xx_adjust_enable_port.

 In function 'vsc73xx_adjust_enable_port',
     inlined from 'vsc73xx_phylink_mac_link_up' at drivers/net/dsa/vitesse-vsc73xx-core.c:891:2:
 drivers/net/dsa/vitesse-vsc73xx-core.c:725:13: warning: 'val' may be used uninitialized [-Wmaybe-uninitialized]
   725 |         val |= VSC73XX_MAC_CFG_RESET;
       |             ^
 drivers/net/dsa/vitesse-vsc73xx-core.c: In function 'vsc73xx_phylink_mac_link_up':
 drivers/net/dsa/vitesse-vsc73xx-core.c:869:13: note: 'val' was declared here
   869 |         u32 val;
       |             ^~~

...
Russell King (Oracle) June 22, 2023, 11:55 a.m. UTC | #3
On Wed, Jun 21, 2023 at 09:12:56PM +0200, Pawel Dembicki wrote:
> +	/* This driver does not make use of the speed, duplex, pause or the
> +	 * advertisement in its mac_config, so it is safe to mark this driver
> +	 * as non-legacy.
> +	 */
> +	config->legacy_pre_march2020 = false;

Great stuff, thanks!

> +static void vsc73xx_phylink_mac_config(struct dsa_switch *ds, int port,
> +				       unsigned int mode,
> +				       const struct phylink_link_state *state)
> +{
> +	struct vsc73xx *vsc = ds->priv;

Nit: normally have a blank line between function variable declarations
and the rest of the function body.

>  	/* Special handling of the CPU-facing port */
>  	if (port == CPU_PORT) {
>  		/* Other ports are already initialized but not this one */
> @@ -775,104 +803,92 @@ static void vsc73xx_adjust_link(struct dsa_switch *ds, int port,
>  			      VSC73XX_ADVPORTM_ENA_GTX |
>  			      VSC73XX_ADVPORTM_DDR_MODE);
>  	}
> +}
>  
> -	/* This is the MAC confiuration that always need to happen
> -	 * after a PHY or the CPU port comes up or down.
> -	 */
> -	if (!phydev->link) {
> -		int maxloop = 10;
> +static void vsc73xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
> +					  unsigned int mode,
> +					  phy_interface_t interface)
> +{
> +	struct vsc73xx *vsc = ds->priv;
> +	u32 val;
>  
> -		dev_dbg(vsc->dev, "port %d: went down\n",
> -			port);
> +	int maxloop = VSC73XX_TABLE_ATTEMPTS;

Reverse christmas-tree for variable declarations, and there shouldn't
be a blank line between them. In any case, I don't think you need
"maxloop" if you adopt my suggestion below.

>  
> -		/* Disable RX on this port */
> -		vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
> -				    VSC73XX_MAC_CFG,
> -				    VSC73XX_MAC_CFG_RX_EN, 0);
> +	dev_dbg(vsc->dev, "port %d: went down\n",
> +		port);
>  
> -		/* Discard packets */
> -		vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
> -				    VSC73XX_ARBDISC, BIT(port), BIT(port));
> +	/* Disable RX on this port */
> +	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
> +			    VSC73XX_MAC_CFG,
> +			    VSC73XX_MAC_CFG_RX_EN, 0);
> +
> +	/* Discard packets */
> +	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
> +			    VSC73XX_ARBDISC, BIT(port), BIT(port));
>  
> -		/* Wait until queue is empty */
> +	/* Wait until queue is empty */
> +	vsc73xx_read(vsc, VSC73XX_BLOCK_ARBITER, 0,
> +		     VSC73XX_ARBEMPTY, &val);
> +	while (!(val & BIT(port))) {
> +		msleep(1);
>  		vsc73xx_read(vsc, VSC73XX_BLOCK_ARBITER, 0,
>  			     VSC73XX_ARBEMPTY, &val);
> -		while (!(val & BIT(port))) {
> -			msleep(1);
> -			vsc73xx_read(vsc, VSC73XX_BLOCK_ARBITER, 0,
> -				     VSC73XX_ARBEMPTY, &val);
> -			if (--maxloop == 0) {
> -				dev_err(vsc->dev,
> -					"timeout waiting for block arbiter\n");
> -				/* Continue anyway */
> -				break;
> -			}
> +		if (--maxloop == 0) {
> +			dev_err(vsc->dev,
> +				"timeout waiting for block arbiter\n");
> +			/* Continue anyway */
> +			break;
>  		}
> +	}

I know you're only moving this code, but I think it would be good to
_first_ have a patch that fixes this polling function:

	int ret, err;
...
		ret = read_poll_timeout(vsc73xx_read, err,
					err < 0 || (val & BIT(port)),
					1000, 10000, false,
					vsc, VSC73XX_BLOCK_ARBITER, 0,
					VSC73XX_ARBEMPTY, &val);
		if (ret != 0)
			dev_err(vsc->dev,
				"timeout waiting for block arbiter\n");
		else if (err < 0)
			dev_err(vsc->dev,
				"error reading arbiter\n");

This avoids the issue that on the last iteration, the code reads the
register, test it, find the condition that's being waiting for is
false, _then_ waits and end up printing the error message - that last
wait is rather useless, and as the arbiter state isn't checked after
waiting, it could be that we had success during the last wait.

> +static void vsc73xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
> +					unsigned int mode,
> +					phy_interface_t interface,
> +					struct phy_device *phydev,
> +					int speed, int duplex,
> +					bool tx_pause, bool rx_pause)
> +{
> +	struct vsc73xx *vsc = ds->priv;
> +	u32 val;
>  
> +	switch (speed) {
> +	case SPEED_1000:
>  		/* Set up default for internal port or external RGMII */
> -		if (phydev->interface == PHY_INTERFACE_MODE_RGMII)
> +		if (interface == PHY_INTERFACE_MODE_RGMII)
>  			val = VSC73XX_MAC_CFG_1000M_F_RGMII;
>  		else
>  			val = VSC73XX_MAC_CFG_1000M_F_PHY;
> -		vsc73xx_adjust_enable_port(vsc, port, phydev, val);
> -	} else if (phydev->speed == SPEED_100) {
> -		if (phydev->duplex == DUPLEX_FULL) {
> -			val = VSC73XX_MAC_CFG_100_10M_F_PHY;
> -			dev_dbg(vsc->dev,
> -				"port %d: 100 Mbit full duplex mode\n",
> -				port);
> -		} else {
> -			val = VSC73XX_MAC_CFG_100_10M_H_PHY;
> -			dev_dbg(vsc->dev,
> -				"port %d: 100 Mbit half duplex mode\n",
> -				port);
> -		}
> -		vsc73xx_adjust_enable_port(vsc, port, phydev, val);
> -	} else if (phydev->speed == SPEED_10) {
> -		if (phydev->duplex == DUPLEX_FULL) {
> +		break;
> +	case SPEED_100:
> +	case SPEED_10:
> +		if (duplex == DUPLEX_FULL)
>  			val = VSC73XX_MAC_CFG_100_10M_F_PHY;
> -			dev_dbg(vsc->dev,
> -				"port %d: 10 Mbit full duplex mode\n",
> -				port);
> -		} else {
> +		else
>  			val = VSC73XX_MAC_CFG_100_10M_H_PHY;
> -			dev_dbg(vsc->dev,
> -				"port %d: 10 Mbit half duplex mode\n",
> -				port);
> -		}
> -		vsc73xx_adjust_enable_port(vsc, port, phydev, val);
> -	} else {
> -		dev_err(vsc->dev,
> -			"could not adjust link: unknown speed\n");
> +		break;
>  	}

Do the dev_dbg() add anything useful over what phylink prints when the
link comes up?

I don't think moving to a switch() statement for this is a good idea.
Given that "val" may be uninitialised, I suspect the following may be
a better solution:

	if (speed == SPEED_1000 || speed == SPEED_100 || speed == SPEED_10) {
		if (speed == SPEED_1000) {
			...
		} else {
			...
		}

		... set VSC73XX_BLOCK_ANALYZER and call
		vsc73xx_adjust_enable_port ...
	}

However, looking at the definitions of the various macros, it seems we
can do a little better by not using the VSC73XX_MAC_CFG_*M_[FH]_*
definitions:

		if (speed == SPEED_1000) {
			val = VSC73XX_MAC_CFG_GIGA_MODE |
			      VSC73XX_MAC_CFG_TX_IPG_1000M;

			if (interface == PHY_INTERFACE_MODE_RGMII)
				val |= VSC73XX_MAC_CFG_CLK_SEL_1000M;
			else
				val |= VSC73XX_MAC_CFG_CLK_SEL_EXT;
		} else {
			val = VSC73XX_MAC_CFG_TX_IPG_100_10M |
			      VSC73XX_MAC_CFG_CLK_SEL_EXT;
		}

		if (duplex == DUPLEX_FULL)
			val |= VSC73XX_MAC_CFG_FDX;

Now, this reveals a question: when operating in RGMII mode, why do we
need VSC73XX_MAC_CFG_CLK_SEL_1000M for 1G, and
VSC73XX_MAC_CFG_CLK_SEL_EXT for 10M and 100M, whereas "PHY" mode always
uses CLK_SEL_EXT ?

Thanks.
Pawel Dembicki June 24, 2023, 4:02 a.m. UTC | #4
czw., 22 cze 2023 o 13:55 Russell King (Oracle)
<linux@armlinux.org.uk> napisał(a):
>
> On Wed, Jun 21, 2023 at 09:12:56PM +0200, Pawel Dembicki wrote:
> > +     /* This driver does not make use of the speed, duplex, pause or the
> > +      * advertisement in its mac_config, so it is safe to mark this driver
> > +      * as non-legacy.
> > +      */
> > +     config->legacy_pre_march2020 = false;
>
> Great stuff, thanks!
>
> > +static void vsc73xx_phylink_mac_config(struct dsa_switch *ds, int port,
> > +                                    unsigned int mode,
> > +                                    const struct phylink_link_state *state)
> > +{
> > +     struct vsc73xx *vsc = ds->priv;
>
> Nit: normally have a blank line between function variable declarations
> and the rest of the function body.
>
> >       /* Special handling of the CPU-facing port */
> >       if (port == CPU_PORT) {
> >               /* Other ports are already initialized but not this one */
> > @@ -775,104 +803,92 @@ static void vsc73xx_adjust_link(struct dsa_switch *ds, int port,
> >                             VSC73XX_ADVPORTM_ENA_GTX |
> >                             VSC73XX_ADVPORTM_DDR_MODE);
> >       }
> > +}
> >
> > -     /* This is the MAC confiuration that always need to happen
> > -      * after a PHY or the CPU port comes up or down.
> > -      */
> > -     if (!phydev->link) {
> > -             int maxloop = 10;
> > +static void vsc73xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
> > +                                       unsigned int mode,
> > +                                       phy_interface_t interface)
> > +{
> > +     struct vsc73xx *vsc = ds->priv;
> > +     u32 val;
> >
> > -             dev_dbg(vsc->dev, "port %d: went down\n",
> > -                     port);
> > +     int maxloop = VSC73XX_TABLE_ATTEMPTS;
>
> Reverse christmas-tree for variable declarations, and there shouldn't
> be a blank line between them. In any case, I don't think you need
> "maxloop" if you adopt my suggestion below.
>
> >
> > -             /* Disable RX on this port */
> > -             vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
> > -                                 VSC73XX_MAC_CFG,
> > -                                 VSC73XX_MAC_CFG_RX_EN, 0);
> > +     dev_dbg(vsc->dev, "port %d: went down\n",
> > +             port);
> >
> > -             /* Discard packets */
> > -             vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
> > -                                 VSC73XX_ARBDISC, BIT(port), BIT(port));
> > +     /* Disable RX on this port */
> > +     vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
> > +                         VSC73XX_MAC_CFG,
> > +                         VSC73XX_MAC_CFG_RX_EN, 0);
> > +
> > +     /* Discard packets */
> > +     vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
> > +                         VSC73XX_ARBDISC, BIT(port), BIT(port));
> >
> > -             /* Wait until queue is empty */
> > +     /* Wait until queue is empty */
> > +     vsc73xx_read(vsc, VSC73XX_BLOCK_ARBITER, 0,
> > +                  VSC73XX_ARBEMPTY, &val);
> > +     while (!(val & BIT(port))) {
> > +             msleep(1);
> >               vsc73xx_read(vsc, VSC73XX_BLOCK_ARBITER, 0,
> >                            VSC73XX_ARBEMPTY, &val);
> > -             while (!(val & BIT(port))) {
> > -                     msleep(1);
> > -                     vsc73xx_read(vsc, VSC73XX_BLOCK_ARBITER, 0,
> > -                                  VSC73XX_ARBEMPTY, &val);
> > -                     if (--maxloop == 0) {
> > -                             dev_err(vsc->dev,
> > -                                     "timeout waiting for block arbiter\n");
> > -                             /* Continue anyway */
> > -                             break;
> > -                     }
> > +             if (--maxloop == 0) {
> > +                     dev_err(vsc->dev,
> > +                             "timeout waiting for block arbiter\n");
> > +                     /* Continue anyway */
> > +                     break;
> >               }
> > +     }
>
> I know you're only moving this code, but I think it would be good to
> _first_ have a patch that fixes this polling function:
>
>         int ret, err;
> ...
>                 ret = read_poll_timeout(vsc73xx_read, err,
>                                         err < 0 || (val & BIT(port)),
>                                         1000, 10000, false,
>                                         vsc, VSC73XX_BLOCK_ARBITER, 0,
>                                         VSC73XX_ARBEMPTY, &val);
>                 if (ret != 0)
>                         dev_err(vsc->dev,
>                                 "timeout waiting for block arbiter\n");
>                 else if (err < 0)
>                         dev_err(vsc->dev,
>                                 "error reading arbiter\n");
>
> This avoids the issue that on the last iteration, the code reads the
> register, test it, find the condition that's being waiting for is
> false, _then_ waits and end up printing the error message - that last
> wait is rather useless, and as the arbiter state isn't checked after
> waiting, it could be that we had success during the last wait.
>

Thank you for the tips. I will prepare additional commit in v2 series.

> > +static void vsc73xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
> > +                                     unsigned int mode,
> > +                                     phy_interface_t interface,
> > +                                     struct phy_device *phydev,
> > +                                     int speed, int duplex,
> > +                                     bool tx_pause, bool rx_pause)
> > +{
> > +     struct vsc73xx *vsc = ds->priv;
> > +     u32 val;
> >
> > +     switch (speed) {
> > +     case SPEED_1000:
> >               /* Set up default for internal port or external RGMII */
> > -             if (phydev->interface == PHY_INTERFACE_MODE_RGMII)
> > +             if (interface == PHY_INTERFACE_MODE_RGMII)
> >                       val = VSC73XX_MAC_CFG_1000M_F_RGMII;
> >               else
> >                       val = VSC73XX_MAC_CFG_1000M_F_PHY;
> > -             vsc73xx_adjust_enable_port(vsc, port, phydev, val);
> > -     } else if (phydev->speed == SPEED_100) {
> > -             if (phydev->duplex == DUPLEX_FULL) {
> > -                     val = VSC73XX_MAC_CFG_100_10M_F_PHY;
> > -                     dev_dbg(vsc->dev,
> > -                             "port %d: 100 Mbit full duplex mode\n",
> > -                             port);
> > -             } else {
> > -                     val = VSC73XX_MAC_CFG_100_10M_H_PHY;
> > -                     dev_dbg(vsc->dev,
> > -                             "port %d: 100 Mbit half duplex mode\n",
> > -                             port);
> > -             }
> > -             vsc73xx_adjust_enable_port(vsc, port, phydev, val);
> > -     } else if (phydev->speed == SPEED_10) {
> > -             if (phydev->duplex == DUPLEX_FULL) {
> > +             break;
> > +     case SPEED_100:
> > +     case SPEED_10:
> > +             if (duplex == DUPLEX_FULL)
> >                       val = VSC73XX_MAC_CFG_100_10M_F_PHY;
> > -                     dev_dbg(vsc->dev,
> > -                             "port %d: 10 Mbit full duplex mode\n",
> > -                             port);
> > -             } else {
> > +             else
> >                       val = VSC73XX_MAC_CFG_100_10M_H_PHY;
> > -                     dev_dbg(vsc->dev,
> > -                             "port %d: 10 Mbit half duplex mode\n",
> > -                             port);
> > -             }
> > -             vsc73xx_adjust_enable_port(vsc, port, phydev, val);
> > -     } else {
> > -             dev_err(vsc->dev,
> > -                     "could not adjust link: unknown speed\n");
> > +             break;
> >       }
>
> Do the dev_dbg() add anything useful over what phylink prints when the
> link comes up?
>
> I don't think moving to a switch() statement for this is a good idea.
> Given that "val" may be uninitialised, I suspect the following may be
> a better solution:
>
>         if (speed == SPEED_1000 || speed == SPEED_100 || speed == SPEED_10) {
>                 if (speed == SPEED_1000) {
>                         ...
>                 } else {
>                         ...
>                 }
>
>                 ... set VSC73XX_BLOCK_ANALYZER and call
>                 vsc73xx_adjust_enable_port ...
>         }
>
> However, looking at the definitions of the various macros, it seems we
> can do a little better by not using the VSC73XX_MAC_CFG_*M_[FH]_*
> definitions:
>
>                 if (speed == SPEED_1000) {
>                         val = VSC73XX_MAC_CFG_GIGA_MODE |
>                               VSC73XX_MAC_CFG_TX_IPG_1000M;
>
>                         if (interface == PHY_INTERFACE_MODE_RGMII)
>                                 val |= VSC73XX_MAC_CFG_CLK_SEL_1000M;
>                         else
>                                 val |= VSC73XX_MAC_CFG_CLK_SEL_EXT;
>                 } else {
>                         val = VSC73XX_MAC_CFG_TX_IPG_100_10M |
>                               VSC73XX_MAC_CFG_CLK_SEL_EXT;
>                 }
>
>                 if (duplex == DUPLEX_FULL)
>                         val |= VSC73XX_MAC_CFG_FDX;
>
> Now, this reveals a question: when operating in RGMII mode, why do we
> need VSC73XX_MAC_CFG_CLK_SEL_1000M for 1G, and
> VSC73XX_MAC_CFG_CLK_SEL_EXT for 10M and 100M, whereas "PHY" mode always
> uses CLK_SEL_EXT ?
>

VSC73XX_MAC_CFG_CLK_SEL_EXT should be used always when phy is used, no
matter what speed is. VSC73XX_MAC_CFG_CLK_SEL_1000M in RGMII mode. It
can be even more simplified:

if (speed == SPEED_1000)
val = VSC73XX_MAC_CFG_GIGA_MODE | VSC73XX_MAC_CFG_TX_IPG_1000M;
else
val = VSC73XX_MAC_CFG_TX_IPG_100_10M;

if (interface == PHY_INTERFACE_MODE_RGMII)
val |= VSC73XX_MAC_CFG_CLK_SEL_1000M;
else
val |= VSC73XX_MAC_CFG_CLK_SEL_EXT;

if (duplex == DUPLEX_FULL)
val |= VSC73XX_MAC_CFG_FDX;

--
Pawel Dembicki
diff mbox series

Patch

diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c
index ae55167ce0a6..e853b57b0bc8 100644
--- a/drivers/net/dsa/vitesse-vsc73xx-core.c
+++ b/drivers/net/dsa/vitesse-vsc73xx-core.c
@@ -39,6 +39,7 @@ 
 #define VSC73XX_BLOCK_SYSTEM	0x7 /* Only subblock 0 */
 
 #define CPU_PORT	6 /* CPU port */
+#define VSC73XX_TABLE_ATTEMPTS	10
 
 /* MAC Block registers */
 #define VSC73XX_MAC_CFG		0x00
@@ -715,8 +716,7 @@  static void vsc73xx_init_port(struct vsc73xx *vsc, int port)
 }
 
 static void vsc73xx_adjust_enable_port(struct vsc73xx *vsc,
-				       int port, struct phy_device *phydev,
-				       u32 initval)
+				       int port, u32 initval)
 {
 	u32 val = initval;
 	u8 seed;
@@ -754,12 +754,40 @@  static void vsc73xx_adjust_enable_port(struct vsc73xx *vsc,
 			    VSC73XX_MAC_CFG_TX_EN | VSC73XX_MAC_CFG_RX_EN);
 }
 
-static void vsc73xx_adjust_link(struct dsa_switch *ds, int port,
-				struct phy_device *phydev)
+static void vsc73xx_phylink_get_caps(struct dsa_switch *ds, int port,
+				     struct phylink_config *config)
 {
-	struct vsc73xx *vsc = ds->priv;
-	u32 val;
+	/* This switch only supports full-duplex at 1Gbps */
+	config->mac_capabilities = MAC_10 | MAC_100 | MAC_1000FD |
+				   MAC_ASYM_PAUSE | MAC_SYM_PAUSE;
 
+	if (port == CPU_PORT) {
+		__set_bit(PHY_INTERFACE_MODE_RGMII,
+			  config->supported_interfaces);
+		__set_bit(PHY_INTERFACE_MODE_GMII,
+			  config->supported_interfaces);
+	} else {
+		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
+			  config->supported_interfaces);
+		/* Compatibility for phylib's default interface type when the
+		 * phy-mode property is absent
+		 */
+		__set_bit(PHY_INTERFACE_MODE_GMII,
+			  config->supported_interfaces);
+	}
+
+	/* This driver does not make use of the speed, duplex, pause or the
+	 * advertisement in its mac_config, so it is safe to mark this driver
+	 * as non-legacy.
+	 */
+	config->legacy_pre_march2020 = false;
+}
+
+static void vsc73xx_phylink_mac_config(struct dsa_switch *ds, int port,
+				       unsigned int mode,
+				       const struct phylink_link_state *state)
+{
+	struct vsc73xx *vsc = ds->priv;
 	/* Special handling of the CPU-facing port */
 	if (port == CPU_PORT) {
 		/* Other ports are already initialized but not this one */
@@ -775,104 +803,92 @@  static void vsc73xx_adjust_link(struct dsa_switch *ds, int port,
 			      VSC73XX_ADVPORTM_ENA_GTX |
 			      VSC73XX_ADVPORTM_DDR_MODE);
 	}
+}
 
-	/* This is the MAC confiuration that always need to happen
-	 * after a PHY or the CPU port comes up or down.
-	 */
-	if (!phydev->link) {
-		int maxloop = 10;
+static void vsc73xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
+					  unsigned int mode,
+					  phy_interface_t interface)
+{
+	struct vsc73xx *vsc = ds->priv;
+	u32 val;
 
-		dev_dbg(vsc->dev, "port %d: went down\n",
-			port);
+	int maxloop = VSC73XX_TABLE_ATTEMPTS;
 
-		/* Disable RX on this port */
-		vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
-				    VSC73XX_MAC_CFG,
-				    VSC73XX_MAC_CFG_RX_EN, 0);
+	dev_dbg(vsc->dev, "port %d: went down\n",
+		port);
 
-		/* Discard packets */
-		vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
-				    VSC73XX_ARBDISC, BIT(port), BIT(port));
+	/* Disable RX on this port */
+	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
+			    VSC73XX_MAC_CFG,
+			    VSC73XX_MAC_CFG_RX_EN, 0);
+
+	/* Discard packets */
+	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
+			    VSC73XX_ARBDISC, BIT(port), BIT(port));
 
-		/* Wait until queue is empty */
+	/* Wait until queue is empty */
+	vsc73xx_read(vsc, VSC73XX_BLOCK_ARBITER, 0,
+		     VSC73XX_ARBEMPTY, &val);
+	while (!(val & BIT(port))) {
+		msleep(1);
 		vsc73xx_read(vsc, VSC73XX_BLOCK_ARBITER, 0,
 			     VSC73XX_ARBEMPTY, &val);
-		while (!(val & BIT(port))) {
-			msleep(1);
-			vsc73xx_read(vsc, VSC73XX_BLOCK_ARBITER, 0,
-				     VSC73XX_ARBEMPTY, &val);
-			if (--maxloop == 0) {
-				dev_err(vsc->dev,
-					"timeout waiting for block arbiter\n");
-				/* Continue anyway */
-				break;
-			}
+		if (--maxloop == 0) {
+			dev_err(vsc->dev,
+				"timeout waiting for block arbiter\n");
+			/* Continue anyway */
+			break;
 		}
+	}
 
-		/* Put this port into reset */
-		vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG,
-			      VSC73XX_MAC_CFG_RESET);
-
-		/* Accept packets again */
-		vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
-				    VSC73XX_ARBDISC, BIT(port), 0);
+	/* Put this port into reset */
+	vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG,
+		      VSC73XX_MAC_CFG_RESET);
 
-		/* Allow backward dropping of frames from this port */
-		vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
-				    VSC73XX_SBACKWDROP, BIT(port), BIT(port));
+	/* Accept packets again */
+	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
+			    VSC73XX_ARBDISC, BIT(port), 0);
 
-		/* Receive mask (disable forwarding) */
-		vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
-				    VSC73XX_RECVMASK, BIT(port), 0);
+	/* Allow backward dropping of frames from this port */
+	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
+			    VSC73XX_SBACKWDROP, BIT(port), BIT(port));
 
-		return;
-	}
+	/* Receive mask (disable forwarding) */
+	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
+			    VSC73XX_RECVMASK, BIT(port), 0);
+}
 
-	/* Figure out what speed was negotiated */
-	if (phydev->speed == SPEED_1000) {
-		dev_dbg(vsc->dev, "port %d: 1000 Mbit mode full duplex\n",
-			port);
+static void vsc73xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
+					unsigned int mode,
+					phy_interface_t interface,
+					struct phy_device *phydev,
+					int speed, int duplex,
+					bool tx_pause, bool rx_pause)
+{
+	struct vsc73xx *vsc = ds->priv;
+	u32 val;
 
+	switch (speed) {
+	case SPEED_1000:
 		/* Set up default for internal port or external RGMII */
-		if (phydev->interface == PHY_INTERFACE_MODE_RGMII)
+		if (interface == PHY_INTERFACE_MODE_RGMII)
 			val = VSC73XX_MAC_CFG_1000M_F_RGMII;
 		else
 			val = VSC73XX_MAC_CFG_1000M_F_PHY;
-		vsc73xx_adjust_enable_port(vsc, port, phydev, val);
-	} else if (phydev->speed == SPEED_100) {
-		if (phydev->duplex == DUPLEX_FULL) {
-			val = VSC73XX_MAC_CFG_100_10M_F_PHY;
-			dev_dbg(vsc->dev,
-				"port %d: 100 Mbit full duplex mode\n",
-				port);
-		} else {
-			val = VSC73XX_MAC_CFG_100_10M_H_PHY;
-			dev_dbg(vsc->dev,
-				"port %d: 100 Mbit half duplex mode\n",
-				port);
-		}
-		vsc73xx_adjust_enable_port(vsc, port, phydev, val);
-	} else if (phydev->speed == SPEED_10) {
-		if (phydev->duplex == DUPLEX_FULL) {
+		break;
+	case SPEED_100:
+	case SPEED_10:
+		if (duplex == DUPLEX_FULL)
 			val = VSC73XX_MAC_CFG_100_10M_F_PHY;
-			dev_dbg(vsc->dev,
-				"port %d: 10 Mbit full duplex mode\n",
-				port);
-		} else {
+		else
 			val = VSC73XX_MAC_CFG_100_10M_H_PHY;
-			dev_dbg(vsc->dev,
-				"port %d: 10 Mbit half duplex mode\n",
-				port);
-		}
-		vsc73xx_adjust_enable_port(vsc, port, phydev, val);
-	} else {
-		dev_err(vsc->dev,
-			"could not adjust link: unknown speed\n");
+		break;
 	}
 
 	/* Enable port (forwarding) in the receieve mask */
 	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
 			    VSC73XX_RECVMASK, BIT(port), BIT(port));
+	vsc73xx_adjust_enable_port(vsc, port, val);
 }
 
 static int vsc73xx_port_enable(struct dsa_switch *ds, int port,
@@ -1043,7 +1059,10 @@  static const struct dsa_switch_ops vsc73xx_ds_ops = {
 	.setup = vsc73xx_setup,
 	.phy_read = vsc73xx_phy_read,
 	.phy_write = vsc73xx_phy_write,
-	.adjust_link = vsc73xx_adjust_link,
+	.phylink_get_caps = vsc73xx_phylink_get_caps,
+	.phylink_mac_config = vsc73xx_phylink_mac_config,
+	.phylink_mac_link_down = vsc73xx_phylink_mac_link_down,
+	.phylink_mac_link_up = vsc73xx_phylink_mac_link_up,
 	.get_strings = vsc73xx_get_strings,
 	.get_ethtool_stats = vsc73xx_get_ethtool_stats,
 	.get_sset_count = vsc73xx_get_sset_count,