Message ID | 20240828095103.132625-7-maxime.chevallier@bootlin.com (mailing list archive) |
---|---|
State | Handled Elsewhere, archived |
Headers | show |
Series | net: ethernet: fs_enet: Cleanup and phylink conversion | expand |
Context | Check | Description |
---|---|---|
snowpatch_ozlabs/github-powerpc_kernel_qemu | success | Successfully ran 21 jobs. |
snowpatch_ozlabs/github-powerpc_sparse | success | Successfully ran 4 jobs. |
snowpatch_ozlabs/github-powerpc_clang | success | Successfully ran 5 jobs. |
On Wed, Aug 28, 2024 at 11:51:02AM +0200, Maxime Chevallier wrote: > +static int fs_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) > +{ > + struct fs_enet_private *fep = netdev_priv(dev); > + > + if (!netif_running(dev)) > + return -EINVAL; Why do you need this check?
Hi Russell, On Wed, 28 Aug 2024 11:38:31 +0100 "Russell King (Oracle)" <linux@armlinux.org.uk> wrote: > On Wed, Aug 28, 2024 at 11:51:02AM +0200, Maxime Chevallier wrote: > > +static int fs_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) > > +{ > > + struct fs_enet_private *fep = netdev_priv(dev); > > + > > + if (!netif_running(dev)) > > + return -EINVAL; > > Why do you need this check? > I included it as the original ioctl was phy_do_ioctl_running(), which includes that check. Is this check irrelevant with phylink ? I could only find macb and xilinx_axienet that do the same check in their ioctl. I can't tell you why that check is there in the first place in that driver, a quick grep search leads back from a major driver rework in 2011, at which point the check was already there... Regards, Maxime
On Wed, Aug 28, 2024 at 01:44:13PM +0200, Maxime Chevallier wrote: > Hi Russell, > > On Wed, 28 Aug 2024 11:38:31 +0100 > "Russell King (Oracle)" <linux@armlinux.org.uk> wrote: > > > On Wed, Aug 28, 2024 at 11:51:02AM +0200, Maxime Chevallier wrote: > > > +static int fs_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) > > > +{ > > > + struct fs_enet_private *fep = netdev_priv(dev); > > > + > > > + if (!netif_running(dev)) > > > + return -EINVAL; > > > > Why do you need this check? > > > > I included it as the original ioctl was phy_do_ioctl_running(), which > includes that check. > > Is this check irrelevant with phylink ? I could only find macb and > xilinx_axienet that do the same check in their ioctl. > > I can't tell you why that check is there in the first place in that > driver, a quick grep search leads back from a major driver rework in > 2011, at which point the check was already there... int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd) { if (pl->phydev) { ... do PHY based access / pass on to phylib ... } else { ... for reads: ... return emulated fixed-phy state if in fixed mode ... return emulated inband state if in inband mode ... for writes: ... ignore writes in fixed and inband modes ... otherwise return -EOPNOTSUPP } } So, if a driver decides to connect the PHY during probe, the PHY will always be accessible. If a driver decides to connect the PHY during .ndo_open, the PHY will only be accessible while the netdev is open, otherwise -EOPNOTSUPP will be returned. What do ethernet drivers return when they're not open or not having a PHY? The answer is pretty random error codes. drivers/net/ethernet/renesas/ravb_main.c- if (!netif_running(ndev)) drivers/net/ethernet/renesas/ravb_main.c- return -EINVAL; drivers/net/ethernet/renesas/ravb_main.c- drivers/net/ethernet/renesas/ravb_main.c- if (!phydev) drivers/net/ethernet/renesas/ravb_main.c- return -ENODEV; ... drivers/net/ethernet/renesas/ravb_main.c: return phy_mii_ioctl(phydev, req, cmd); drivers/net/ethernet/renesas/rswitch.c- if (!netif_running(ndev)) drivers/net/ethernet/renesas/rswitch.c- return -EINVAL; drivers/net/ethernet/8390/ax88796.c- if (!netif_running(dev)) drivers/net/ethernet/8390/ax88796.c- return -EINVAL; drivers/net/ethernet/8390/ax88796.c- drivers/net/ethernet/8390/ax88796.c- if (!phy_dev) drivers/net/ethernet/8390/ax88796.c- return -ENODEV; drivers/net/ethernet/marvell/mv643xx_eth.c- if (!dev->phydev) drivers/net/ethernet/marvell/mv643xx_eth.c- return -ENOTSUPP; drivers/net/ethernet/xilinx/xilinx_emaclite.c- if (!dev->phydev || !netif_running(dev)) drivers/net/ethernet/xilinx/xilinx_emaclite.c- return -EINVAL; drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c- if (!(netif_running(netdev))) drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_main.c- return -EINVAL; drivers/net/ethernet/actions/owl-emac.c- if (!netif_running(netdev)) drivers/net/ethernet/actions/owl-emac.c- return -EINVAL; drivers/net/ethernet/engleder/tsnep_main.c- if (!netif_running(netdev)) drivers/net/ethernet/engleder/tsnep_main.c- return -EINVAL; drivers/net/ethernet/ti/davinci_emac.c- if (!(netif_running(ndev))) drivers/net/ethernet/ti/davinci_emac.c- return -EINVAL; drivers/net/ethernet/ti/davinci_emac.c- if (ndev->phydev) drivers/net/ethernet/ti/davinci_emac.c: return phy_mii_ioctl(ndev->phydev, ifrq, cmd); drivers/net/ethernet/ti/davinci_emac.c- else drivers/net/ethernet/ti/davinci_emac.c- return -EOPNOTSUPP; drivers/net/ethernet/ti/netcp_ethss.c- if (phy) drivers/net/ethernet/ti/netcp_ethss.c: return phy_mii_ioctl(phy, req, cmd); drivers/net/ethernet/ti/netcp_ethss.c- drivers/net/ethernet/ti/netcp_ethss.c- return -EOPNOTSUPP; drivers/net/ethernet/ti/cpsw_priv.c- if (phy) drivers/net/ethernet/ti/cpsw_priv.c: return phy_mii_ioctl(phy, req, cmd); drivers/net/ethernet/ti/cpsw_priv.c- drivers/net/ethernet/ti/cpsw_priv.c- return -EOPNOTSUPP; drivers/net/ethernet/xscale/ixp4xx_eth.c- if (!netif_running(dev)) drivers/net/ethernet/xscale/ixp4xx_eth.c- return -EINVAL; drivers/net/ethernet/freescale/gianfar.c- if (!netif_running(dev)) drivers/net/ethernet/freescale/gianfar.c- return -EINVAL; ... drivers/net/ethernet/freescale/gianfar.c- if (!phydev) drivers/net/ethernet/freescale/gianfar.c- return -ENODEV; drivers/net/ethernet/freescale/gianfar.c- drivers/net/ethernet/freescale/gianfar.c: return phy_mii_ioctl(phydev, rq, cmd); drivers/net/ethernet/freescale/ucc_geth.c- if (!netif_running(dev)) drivers/net/ethernet/freescale/ucc_geth.c- return -EINVAL; drivers/net/ethernet/freescale/ucc_geth.c- drivers/net/ethernet/freescale/ucc_geth.c- if (!ugeth->phydev) drivers/net/ethernet/freescale/ucc_geth.c- return -ENODEV; drivers/net/ethernet/mediatek/mtk_star_emac.c- if (!netif_running(ndev)) drivers/net/ethernet/mediatek/mtk_star_emac.c- return -EINVAL; drivers/net/ethernet/microchip/lan743x_main.c- if (!netif_running(netdev)) drivers/net/ethernet/microchip/lan743x_main.c- return -EINVAL; drivers/net/ethernet/ethoc.c- if (!netif_running(dev)) drivers/net/ethernet/ethoc.c- return -EINVAL; drivers/net/ethernet/broadcom/b44.c- int err = -EINVAL; drivers/net/ethernet/broadcom/b44.c- drivers/net/ethernet/broadcom/b44.c- if (!netif_running(dev)) drivers/net/ethernet/broadcom/b44.c- goto out; drivers/net/ethernet/broadcom/sb1250-mac.c- if (!netif_running(dev) || !sc->phy_dev) drivers/net/ethernet/broadcom/sb1250-mac.c- return -EINVAL; drivers/net/usb/smsc95xx.c- if (!netif_running(netdev)) drivers/net/usb/smsc95xx.c- return -EINVAL; Of 28 drivers that call phy_mii_ioctl(): - 17 drivers return EINVAL if !netif_running(). - 11 drivers do not check netif_running(). and if there's no phydev: - 4 drivers return ENODEV - 3 drivers return EOPNOTSUPP (plus all drivers converted to phylink) - 2 drivers return EINVAL - 1 driver returns ENOTSUPP Phylib itself doesn't want NULL passed to phy_mii_ioctl(), so its up to the driver to trap this if its calling phy_mii_ioctl(). However, phylib also provides phy_do_ioctl() for hooking directly into .ndo_eth_ioctl, which is: int phy_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { if (!dev->phydev) return -ENODEV; return phy_mii_ioctl(dev->phydev, ifr, cmd); } then there's (as you point out) phy_do_ioctl_running(), which is: int phy_do_ioctl_running(struct net_device *dev, struct ifreq *ifr, int cmd) { if (!netif_running(dev)) return -ENODEV; return phy_do_ioctl(dev, ifr, cmd); } and this returns ENODEV if the netif isn't running, not EINVAL which the majority of net drivers that manually check are doing. Maybe phylib has the error code wrong? In any case, I think it's pretty clear that there's no single "right" error code for these cases, everyone just puts up on a piece of paper with a donkey the names of various error codes, and while blindfolded sticks a pin in to find the "right" error code to use! So, I don't see any point in debating what is the "One True Right Error Code" for these conditions. Now, the question is... why do drivers do this netif_running() check? Is it because "other drivers do" or is it because there's a valid reason. There's no way to know, no one ever documents why the check is there - and based on your response, it's "because other drivers do". Without looking at the history, I wouldn't make any assumption about using phy_do_ioctl_running() - that could be the result of a drive-by cleanup patch converting code to use that helper. At this point... this email has eaten up a substantial amount of time, and I can't spend anymore time in front of the screen today so that's the end of my contributions for today. Sorry.
Hello Russell, On Wed, 28 Aug 2024 14:54:57 +0100 "Russell King (Oracle)" <linux@armlinux.org.uk> wrote: > On Wed, Aug 28, 2024 at 01:44:13PM +0200, Maxime Chevallier wrote: > > Hi Russell, > > > > On Wed, 28 Aug 2024 11:38:31 +0100 > > "Russell King (Oracle)" <linux@armlinux.org.uk> wrote: > > > > > On Wed, Aug 28, 2024 at 11:51:02AM +0200, Maxime Chevallier wrote: > > > > +static int fs_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) > > > > +{ > > > > + struct fs_enet_private *fep = netdev_priv(dev); > > > > + > > > > + if (!netif_running(dev)) > > > > + return -EINVAL; > > > > > > Why do you need this check? > > > > > > > I included it as the original ioctl was phy_do_ioctl_running(), which > > includes that check. > > > > Is this check irrelevant with phylink ? I could only find macb and > > xilinx_axienet that do the same check in their ioctl. > > > > I can't tell you why that check is there in the first place in that > > driver, a quick grep search leads back from a major driver rework in > > 2011, at which point the check was already there... > > int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd) > { > if (pl->phydev) { > ... do PHY based access / pass on to phylib ... > } else { > ... for reads: > ... return emulated fixed-phy state if in fixed mode > ... return emulated inband state if in inband mode > ... for writes: > ... ignore writes in fixed and inband modes > ... otherwise return -EOPNOTSUPP > } > } > > So, if a driver decides to connect the PHY during probe, the PHY will > always be accessible. > > If a driver decides to connect the PHY during .ndo_open, the PHY will > only be accessible while the netdev is open, otherwise -EOPNOTSUPP > will be returned. That makes sense, so there's no point keeping that check then. I'll update the patch, thanks for this clarification. [...] > At this point... this email has eaten up a substantial amount of time, > and I can't spend anymore time in front of the screen today so that's > the end of my contributions for today. Sorry. I've been in the same rabbit-hole today debating in my head whether or not to add this check, I'm sorry that I dragged you in there... With what you stressed-out, I have a good enough justification to drop the check in the current patch. As for the current situation with the ioctl return codes, there indeed room for lots of improvements. For drivers that simply forward the ioctl to phylib/phylink, I think we could pretty easily add some consistency on the return code, provided we agree on the proper one to return. Thanks for your time, Maxime
On Wed, Aug 28, 2024 at 11:51:02AM +0200, Maxime Chevallier wrote: > fs_enet is a quite old but still used Ethernet driver found on some NXP > devices. It has support for 10/100 Mbps ethernet, with half and full > duplex. Some variants of it can use RMII, while other integrations are > MII-only. > > Add phylink support, thus removing custom fixed-link hanldling. > > This also allows removing some internal flags such as the use_rmii flag. > > Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com> Hi Maxime, Some minor issues from my side: not a full review by any stretch of the imagination. ... > @@ -911,7 +894,7 @@ static int fs_enet_probe(struct platform_device *ofdev) > if (!IS_ERR(clk)) { > ret = clk_prepare_enable(clk); > if (ret) > - goto out_deregister_fixed_link; > + goto out_phylink; > > fpi->clk_per = clk; > } This goto will result in a dereference of fep. But fep is not initialised until the following line, which appears a little below this hunk. fep = netdev_priv(ndev); This goto will also result in the function returning without releasing clk. Both flagged by Smatch. > @@ -936,6 +919,26 @@ static int fs_enet_probe(struct platform_device *ofdev) > fep->fpi = fpi; > fep->ops = ops; > > + fep->phylink_config.dev = &ndev->dev; > + fep->phylink_config.type = PHYLINK_NETDEV; > + fep->phylink_config.mac_capabilities = MAC_10 | MAC_100; > + > + __set_bit(PHY_INTERFACE_MODE_MII, > + fep->phylink_config.supported_interfaces); > + > + if (of_device_is_compatible(ofdev->dev.of_node, "fsl,mpc5125-fec")) > + __set_bit(PHY_INTERFACE_MODE_RMII, > + fep->phylink_config.supported_interfaces); > + > + phylink = phylink_create(&fep->phylink_config, dev_fwnode(fep->dev), > + phy_mode, &fs_enet_phylink_mac_ops); > + if (IS_ERR(phylink)) { > + ret = PTR_ERR(phylink); > + goto out_free_fpi; This also appears to leak clk, as well as ndev. I didn't look for other cases. > + } > + > + fep->phylink = phylink; > + > ret = fep->ops->setup_data(ndev); > if (ret) > goto out_free_dev; > @@ -968,8 +971,6 @@ static int fs_enet_probe(struct platform_device *ofdev) > > ndev->ethtool_ops = &fs_ethtool_ops; > > - netif_carrier_off(ndev); > - > ndev->features |= NETIF_F_SG; > > ret = register_netdev(ndev); > @@ -988,10 +989,8 @@ static int fs_enet_probe(struct platform_device *ofdev) > free_netdev(ndev); > out_put: > clk_disable_unprepare(fpi->clk_per); > -out_deregister_fixed_link: > - of_node_put(fpi->phy_node); > - if (of_phy_is_fixed_link(ofdev->dev.of_node)) > - of_phy_deregister_fixed_link(ofdev->dev.of_node); > +out_phylink: > + phylink_destroy(fep->phylink); > out_free_fpi: > kfree(fpi); > return ret; ...
Hello Simon, On Wed, 28 Aug 2024 17:32:24 +0100 Simon Horman <horms@kernel.org> wrote: > On Wed, Aug 28, 2024 at 11:51:02AM +0200, Maxime Chevallier wrote: > > fs_enet is a quite old but still used Ethernet driver found on some NXP > > devices. It has support for 10/100 Mbps ethernet, with half and full > > duplex. Some variants of it can use RMII, while other integrations are > > MII-only. > > > > Add phylink support, thus removing custom fixed-link hanldling. > > > > This also allows removing some internal flags such as the use_rmii flag. > > > > Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com> > > Hi Maxime, > > Some minor issues from my side: not a full review by any stretch of > the imagination. > > ... > > > @@ -911,7 +894,7 @@ static int fs_enet_probe(struct platform_device *ofdev) > > if (!IS_ERR(clk)) { > > ret = clk_prepare_enable(clk); > > if (ret) > > - goto out_deregister_fixed_link; > > + goto out_phylink; > > > > fpi->clk_per = clk; > > } > > This goto will result in a dereference of fep. > But fep is not initialised until the following line, > which appears a little below this hunk. > > fep = netdev_priv(ndev); Nice catch, the goto should rather go to out_free_fpi. > > This goto will also result in the function returning without > releasing clk. ah yes, it's never disabled_unprepared, the phylink cleanup label is at the wrong spot. I'll include a patch in the next iteration to make use of devm_clk_get_enabled(), that should simplify all of that. > Both flagged by Smatch. > > > @@ -936,6 +919,26 @@ static int fs_enet_probe(struct platform_device *ofdev) > > fep->fpi = fpi; > > fep->ops = ops; > > > > + fep->phylink_config.dev = &ndev->dev; > > + fep->phylink_config.type = PHYLINK_NETDEV; > > + fep->phylink_config.mac_capabilities = MAC_10 | MAC_100; > > + > > + __set_bit(PHY_INTERFACE_MODE_MII, > > + fep->phylink_config.supported_interfaces); > > + > > + if (of_device_is_compatible(ofdev->dev.of_node, "fsl,mpc5125-fec")) > > + __set_bit(PHY_INTERFACE_MODE_RMII, > > + fep->phylink_config.supported_interfaces); > > + > > + phylink = phylink_create(&fep->phylink_config, dev_fwnode(fep->dev), > > + phy_mode, &fs_enet_phylink_mac_ops); > > + if (IS_ERR(phylink)) { > > + ret = PTR_ERR(phylink); > > + goto out_free_fpi; > > This also appears to leak clk, as well as ndev. Thanks, will be addressed in V2. > I didn't look for other cases. I'll go over the cleanup path, thanks for checking this ! Thanks, Maxime
diff --git a/drivers/net/ethernet/freescale/fs_enet/Kconfig b/drivers/net/ethernet/freescale/fs_enet/Kconfig index 7f20840fde07..57013bf14d7c 100644 --- a/drivers/net/ethernet/freescale/fs_enet/Kconfig +++ b/drivers/net/ethernet/freescale/fs_enet/Kconfig @@ -3,7 +3,7 @@ config FS_ENET tristate "Freescale Ethernet Driver" depends on NET_VENDOR_FREESCALE && (CPM1 || CPM2 || PPC_MPC512x) select MII - select PHYLIB + select PHYLINK config FS_ENET_MPC5121_FEC def_bool y if (FS_ENET && PPC_MPC512x) diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index caca81b3ccb6..89ad9697f225 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -32,11 +32,13 @@ #include <linux/fs.h> #include <linux/platform_device.h> #include <linux/phy.h> +#include <linux/phylink.h> #include <linux/property.h> #include <linux/of.h> #include <linux/of_mdio.h> #include <linux/of_net.h> #include <linux/pgtable.h> +#include <linux/rtnetlink.h> #include <linux/vmalloc.h> #include <asm/irq.h> @@ -69,6 +71,16 @@ static void fs_set_multicast_list(struct net_device *dev) (*fep->ops->set_multicast_list)(dev); } +static int fs_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct fs_enet_private *fep = netdev_priv(dev); + + if (!netif_running(dev)) + return -EINVAL; + + return phylink_mii_ioctl(fep->phylink, ifr, cmd); +} + static void skb_align(struct sk_buff *skb, int align) { int off = ((unsigned long)skb->data) & (align - 1); @@ -582,15 +594,12 @@ static void fs_timeout_work(struct work_struct *work) dev->stats.tx_errors++; - spin_lock_irqsave(&fep->lock, flags); + rtnl_lock(); + phylink_stop(fep->phylink); + phylink_start(fep->phylink); + rtnl_unlock(); - if (dev->flags & IFF_UP) { - phy_stop(dev->phydev); - (*fep->ops->stop)(dev); - (*fep->ops->restart)(dev); - } - - phy_start(dev->phydev); + spin_lock_irqsave(&fep->lock, flags); wake = fep->tx_free >= MAX_SKB_FRAGS && !(CBDR_SC(fep->cur_tx) & BD_ENET_TX_READY); spin_unlock_irqrestore(&fep->lock, flags); @@ -606,74 +615,37 @@ static void fs_timeout(struct net_device *dev, unsigned int txqueue) schedule_work(&fep->timeout_work); } -/* generic link-change handler - should be sufficient for most cases */ -static void generic_adjust_link(struct net_device *dev) +static void fs_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) { - struct fs_enet_private *fep = netdev_priv(dev); - struct phy_device *phydev = dev->phydev; - int new_state = 0; - - if (phydev->link) { - /* adjust to duplex mode */ - if (phydev->duplex != fep->oldduplex) { - new_state = 1; - fep->oldduplex = phydev->duplex; - } - - if (phydev->speed != fep->oldspeed) { - new_state = 1; - fep->oldspeed = phydev->speed; - } - - if (!fep->oldlink) { - new_state = 1; - fep->oldlink = 1; - } - - if (new_state) - fep->ops->restart(dev); - } else if (fep->oldlink) { - new_state = 1; - fep->oldlink = 0; - fep->oldspeed = 0; - fep->oldduplex = -1; - } + struct net_device *ndev = to_net_dev(config->dev); + struct fs_enet_private *fep = netdev_priv(ndev); + unsigned long flags; - if (new_state && netif_msg_link(fep)) - phy_print_status(phydev); + spin_lock_irqsave(&fep->lock, flags); + fep->ops->restart(ndev, interface, speed, duplex); + spin_unlock_irqrestore(&fep->lock, flags); } -static void fs_adjust_link(struct net_device *dev) +static void fs_mac_link_down(struct phylink_config *config, + unsigned int mode, phy_interface_t interface) { - struct fs_enet_private *fep = netdev_priv(dev); + struct net_device *ndev = to_net_dev(config->dev); + struct fs_enet_private *fep = netdev_priv(ndev); unsigned long flags; spin_lock_irqsave(&fep->lock, flags); - generic_adjust_link(dev); + fep->ops->stop(ndev); spin_unlock_irqrestore(&fep->lock, flags); } -static int fs_init_phy(struct net_device *dev) +static void fs_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) { - struct fs_enet_private *fep = netdev_priv(dev); - struct phy_device *phydev; - phy_interface_t iface; - - fep->oldlink = 0; - fep->oldspeed = 0; - fep->oldduplex = -1; - - iface = fep->fpi->use_rmii ? - PHY_INTERFACE_MODE_RMII : PHY_INTERFACE_MODE_MII; - - phydev = of_phy_connect(dev, fep->fpi->phy_node, &fs_adjust_link, 0, - iface); - if (!phydev) { - dev_err(&dev->dev, "Could not attach to PHY\n"); - return -ENODEV; - } - - return 0; + /* Nothing to do */ } static int fs_enet_open(struct net_device *dev) @@ -698,13 +670,13 @@ static int fs_enet_open(struct net_device *dev) return -EINVAL; } - err = fs_init_phy(dev); + err = phylink_of_phy_connect(fep->phylink, fep->dev->of_node, 0); if (err) { free_irq(fep->interrupt, dev); napi_disable(&fep->napi); return err; } - phy_start(dev->phydev); + phylink_start(fep->phylink); netif_start_queue(dev); @@ -717,19 +689,18 @@ static int fs_enet_close(struct net_device *dev) unsigned long flags; netif_stop_queue(dev); - netif_carrier_off(dev); napi_disable(&fep->napi); cancel_work_sync(&fep->timeout_work); - phy_stop(dev->phydev); + phylink_stop(fep->phylink); spin_lock_irqsave(&fep->lock, flags); spin_lock(&fep->tx_lock); (*fep->ops->stop)(dev); spin_unlock(&fep->tx_lock); spin_unlock_irqrestore(&fep->lock, flags); + phylink_disconnect_phy(fep->phylink); /* release any irqs */ - phy_disconnect(dev->phydev); free_irq(fep->interrupt, dev); return 0; @@ -817,6 +788,22 @@ static int fs_set_tunable(struct net_device *dev, return ret; } +static int fs_ethtool_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + struct fs_enet_private *fep = netdev_priv(dev); + + return phylink_ethtool_ksettings_set(fep->phylink, cmd); +} + +static int fs_ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + struct fs_enet_private *fep = netdev_priv(dev); + + return phylink_ethtool_ksettings_get(fep->phylink, cmd); +} + static const struct ethtool_ops fs_ethtool_ops = { .get_drvinfo = fs_get_drvinfo, .get_regs_len = fs_get_regs_len, @@ -826,8 +813,8 @@ static const struct ethtool_ops fs_ethtool_ops = { .set_msglevel = fs_set_msglevel, .get_regs = fs_get_regs, .get_ts_info = ethtool_op_get_ts_info, - .get_link_ksettings = phy_ethtool_get_link_ksettings, - .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_link_ksettings = fs_ethtool_get_link_ksettings, + .set_link_ksettings = fs_ethtool_set_link_ksettings, .get_tunable = fs_get_tunable, .set_tunable = fs_set_tunable, }; @@ -844,7 +831,7 @@ static const struct net_device_ops fs_enet_netdev_ops = { .ndo_start_xmit = fs_enet_start_xmit, .ndo_tx_timeout = fs_timeout, .ndo_set_rx_mode = fs_set_multicast_list, - .ndo_eth_ioctl = phy_do_ioctl_running, + .ndo_eth_ioctl = fs_eth_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -852,14 +839,21 @@ static const struct net_device_ops fs_enet_netdev_ops = { #endif }; +static const struct phylink_mac_ops fs_enet_phylink_mac_ops = { + .mac_config = fs_mac_config, + .mac_link_down = fs_mac_link_down, + .mac_link_up = fs_mac_link_up, +}; + static int fs_enet_probe(struct platform_device *ofdev) { - int err, privsize, len, ret = -ENODEV; - const char *phy_connection_type; + int privsize, len, ret = -ENODEV; struct fs_platform_info *fpi; struct fs_enet_private *fep; + phy_interface_t phy_mode; const struct fs_ops *ops; struct net_device *ndev; + struct phylink *phylink; const u32 *data; struct clk *clk; @@ -879,29 +873,18 @@ static int fs_enet_probe(struct platform_device *ofdev) fpi->cp_command = *data; } + ret = of_get_phy_mode(ofdev->dev.of_node, &phy_mode); + if (ret) { + /* For compatibility, if the mode isn't specified in DT, + * assume MII + */ + phy_mode = PHY_INTERFACE_MODE_MII; + } + fpi->rx_ring = RX_RING_SIZE; fpi->tx_ring = TX_RING_SIZE; fpi->rx_copybreak = 240; fpi->napi_weight = 17; - fpi->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0); - if (!fpi->phy_node && of_phy_is_fixed_link(ofdev->dev.of_node)) { - err = of_phy_register_fixed_link(ofdev->dev.of_node); - if (err) - goto out_free_fpi; - - /* In the case of a fixed PHY, the DT node associated - * to the PHY is the Ethernet MAC DT node. - */ - fpi->phy_node = of_node_get(ofdev->dev.of_node); - } - - if (of_device_is_compatible(ofdev->dev.of_node, "fsl,mpc5125-fec")) { - phy_connection_type = of_get_property(ofdev->dev.of_node, - "phy-connection-type", - NULL); - if (phy_connection_type && !strcmp("rmii", phy_connection_type)) - fpi->use_rmii = 1; - } /* make clock lookup non-fatal (the driver is shared among platforms), * but require enable to succeed when a clock was specified/found, @@ -911,7 +894,7 @@ static int fs_enet_probe(struct platform_device *ofdev) if (!IS_ERR(clk)) { ret = clk_prepare_enable(clk); if (ret) - goto out_deregister_fixed_link; + goto out_phylink; fpi->clk_per = clk; } @@ -936,6 +919,26 @@ static int fs_enet_probe(struct platform_device *ofdev) fep->fpi = fpi; fep->ops = ops; + fep->phylink_config.dev = &ndev->dev; + fep->phylink_config.type = PHYLINK_NETDEV; + fep->phylink_config.mac_capabilities = MAC_10 | MAC_100; + + __set_bit(PHY_INTERFACE_MODE_MII, + fep->phylink_config.supported_interfaces); + + if (of_device_is_compatible(ofdev->dev.of_node, "fsl,mpc5125-fec")) + __set_bit(PHY_INTERFACE_MODE_RMII, + fep->phylink_config.supported_interfaces); + + phylink = phylink_create(&fep->phylink_config, dev_fwnode(fep->dev), + phy_mode, &fs_enet_phylink_mac_ops); + if (IS_ERR(phylink)) { + ret = PTR_ERR(phylink); + goto out_free_fpi; + } + + fep->phylink = phylink; + ret = fep->ops->setup_data(ndev); if (ret) goto out_free_dev; @@ -968,8 +971,6 @@ static int fs_enet_probe(struct platform_device *ofdev) ndev->ethtool_ops = &fs_ethtool_ops; - netif_carrier_off(ndev); - ndev->features |= NETIF_F_SG; ret = register_netdev(ndev); @@ -988,10 +989,8 @@ static int fs_enet_probe(struct platform_device *ofdev) free_netdev(ndev); out_put: clk_disable_unprepare(fpi->clk_per); -out_deregister_fixed_link: - of_node_put(fpi->phy_node); - if (of_phy_is_fixed_link(ofdev->dev.of_node)) - of_phy_deregister_fixed_link(ofdev->dev.of_node); +out_phylink: + phylink_destroy(fep->phylink); out_free_fpi: kfree(fpi); return ret; @@ -1007,10 +1006,8 @@ static void fs_enet_remove(struct platform_device *ofdev) fep->ops->free_bd(ndev); fep->ops->cleanup_data(ndev); dev_set_drvdata(fep->dev, NULL); - of_node_put(fep->fpi->phy_node); clk_disable_unprepare(fep->fpi->clk_per); - if (of_phy_is_fixed_link(ofdev->dev.of_node)) - of_phy_deregister_fixed_link(ofdev->dev.of_node); + phylink_destroy(fep->phylink); free_netdev(ndev); } diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet.h b/drivers/net/ethernet/freescale/fs_enet/fs_enet.h index 781f506c933c..2c436cdd4626 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet.h +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet.h @@ -8,6 +8,7 @@ #include <linux/types.h> #include <linux/list.h> #include <linux/phy.h> +#include <linux/phylink.h> #include <linux/dma-mapping.h> #ifdef CONFIG_CPM1 @@ -77,7 +78,8 @@ struct fs_ops { void (*free_bd)(struct net_device *dev); void (*cleanup_data)(struct net_device *dev); void (*set_multicast_list)(struct net_device *dev); - void (*restart)(struct net_device *dev); + void (*restart)(struct net_device *dev, phy_interface_t interface, + int speed, int duplex); void (*stop)(struct net_device *dev); void (*napi_clear_event)(struct net_device *dev); void (*napi_enable)(struct net_device *dev); @@ -113,14 +115,10 @@ struct fs_platform_info { u32 dpram_offset; - struct device_node *phy_node; - int rx_ring, tx_ring; /* number of buffers on rx */ int rx_copybreak; /* limit we copy small frames */ int napi_weight; /* NAPI weight */ - int use_rmii; /* use RMII mode */ - struct clk *clk_per; /* 'per' clock for register access */ }; @@ -146,10 +144,10 @@ struct fs_enet_private { cbd_t __iomem *cur_tx; int tx_free; u32 msg_enable; + struct phylink *phylink; + struct phylink_config phylink_config; int interrupt; - int oldduplex, oldspeed, oldlink; /* current settings */ - /* event masks */ u32 ev_napi; /* mask of NAPI events */ u32 ev; /* event mask */ diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c index 056909156b4f..3cb88fd91eaf 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c @@ -236,7 +236,8 @@ static void set_multicast_list(struct net_device *dev) set_promiscuous_mode(dev); } -static void restart(struct net_device *dev) +static void restart(struct net_device *dev, phy_interface_t interface, + int speed, int duplex) { struct fs_enet_private *fep = netdev_priv(dev); const struct fs_platform_info *fpi = fep->fpi; @@ -360,8 +361,8 @@ static void restart(struct net_device *dev) fs_init_bds(dev); /* adjust to speed (for RMII mode) */ - if (fpi->use_rmii) { - if (dev->phydev->speed == SPEED_100) + if (interface == PHY_INTERFACE_MODE_RMII) { + if (speed == SPEED_100) C8(fcccp, fcc_gfemr, 0x20); else S8(fcccp, fcc_gfemr, 0x20); @@ -383,11 +384,11 @@ static void restart(struct net_device *dev) W32(fccp, fcc_fpsmr, FCC_PSMR_ENCRC); - if (fpi->use_rmii) + if (interface == PHY_INTERFACE_MODE_RMII) S32(fccp, fcc_fpsmr, FCC_PSMR_RMII); /* adjust to duplex mode */ - if (dev->phydev->duplex == DUPLEX_FULL) + if (duplex == DUPLEX_FULL) S32(fccp, fcc_fpsmr, FCC_PSMR_FDE | FCC_PSMR_LPB); else C32(fccp, fcc_fpsmr, FCC_PSMR_FDE | FCC_PSMR_LPB); diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c index 855ee9e3f042..9442847efa13 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c @@ -221,7 +221,8 @@ static void set_multicast_list(struct net_device *dev) set_promiscuous_mode(dev); } -static void restart(struct net_device *dev) +static void restart(struct net_device *dev, phy_interface_t interface, + int speed, int duplex) { struct fs_enet_private *fep = netdev_priv(dev); struct fec __iomem *fecp = fep->fec.fecp; @@ -303,13 +304,13 @@ static void restart(struct net_device *dev) * Only set MII/RMII mode - do not touch maximum frame length * configured before. */ - FS(fecp, r_cntrl, fpi->use_rmii ? - FEC_RCNTRL_RMII_MODE : FEC_RCNTRL_MII_MODE); + FS(fecp, r_cntrl, interface == PHY_INTERFACE_MODE_RMII ? + FEC_RCNTRL_RMII_MODE : FEC_RCNTRL_MII_MODE); #endif /* * adjust to duplex mode */ - if (dev->phydev->duplex == DUPLEX_FULL) { + if (duplex == DUPLEX_FULL) { FC(fecp, r_cntrl, FEC_RCNTRL_DRT); FS(fecp, x_cntrl, FEC_TCNTRL_FDEN); /* FD enable */ } else { diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c index 9e5e29312c27..846aafff6951 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c @@ -227,7 +227,8 @@ static void set_multicast_list(struct net_device *dev) * change. This only happens when switching between half and full * duplex. */ -static void restart(struct net_device *dev) +static void restart(struct net_device *dev, phy_interface_t interface, + int speed, int duplex) { struct fs_enet_private *fep = netdev_priv(dev); scc_t __iomem *sccp = fep->scc.sccp; @@ -338,7 +339,7 @@ static void restart(struct net_device *dev) W16(sccp, scc_psmr, SCC_PSMR_ENCRC | SCC_PSMR_NIB22); /* Set full duplex mode if needed */ - if (dev->phydev->duplex == DUPLEX_FULL) + if (duplex == DUPLEX_FULL) S16(sccp, scc_psmr, SCC_PSMR_LPB | SCC_PSMR_FDE); /* Restore multicast and promiscuous settings */
fs_enet is a quite old but still used Ethernet driver found on some NXP devices. It has support for 10/100 Mbps ethernet, with half and full duplex. Some variants of it can use RMII, while other integrations are MII-only. Add phylink support, thus removing custom fixed-link hanldling. This also allows removing some internal flags such as the use_rmii flag. Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com> --- .../net/ethernet/freescale/fs_enet/Kconfig | 2 +- .../ethernet/freescale/fs_enet/fs_enet-main.c | 203 +++++++++--------- .../net/ethernet/freescale/fs_enet/fs_enet.h | 12 +- .../net/ethernet/freescale/fs_enet/mac-fcc.c | 11 +- .../net/ethernet/freescale/fs_enet/mac-fec.c | 9 +- .../net/ethernet/freescale/fs_enet/mac-scc.c | 5 +- 6 files changed, 120 insertions(+), 122 deletions(-)