RE: [PATCH 3/3] net: fec: Reset ethernet PHY whenever the enet_out clock is being enabled

Duan Andy
2015-12-01T02:31:50Z

From: Lothar Waßmann <LW@KARO-electronics.de> Sent: Monday, November 30, 2015 7:33 PM
> To: Andrew Lunn; David S. Miller; Estevam Fabio-R49496; Greg Ungerer;
> Kevin Hao; Lothar Waßmann; Lucas Stach; Duan Fugang-B38611; Philippe
> Reynes; Richard Cochran; Russell King; Sascha Hauer; Stefan Agner; linux-
> kernel@vger.kernel.org; netdev@vger.kernel.org; Jeff Kirsher; Uwe Kleine-
> König
> Subject: [PATCH 3/3] net: fec: Reset ethernet PHY whenever the enet_out
> clock is being enabled
>
> If a PHY uses ENET_OUT as reference clock, it may need a RESET to get
> functional after the clock had been disabled.
>
> Failure to do this results in the link state constantly toggling between
> up and down:
> fec 02188000.ethernet eth0: Link is Up - 100Mbps/Full - flow control
> rx/tx fec 02188000.ethernet eth0: Link is Down fec 02188000.ethernet eth0:
> Link is Up - 100Mbps/Full - flow control rx/tx fec 02188000.ethernet eth0:
> Link is Down [...]
>
> Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
> ---
> drivers/net/ethernet/freescale/fec.h | 1 +
> drivers/net/ethernet/freescale/fec_main.c | 46 ++++++++++++++++++++++++-
> ------
> 2 files changed, 37 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/fec.h
> b/drivers/net/ethernet/freescale/fec.h
> index 99d33e2..8ab4f7f 100644
> --- a/drivers/net/ethernet/freescale/fec.h
> +++ b/drivers/net/ethernet/freescale/fec.h
> @@ -519,6 +519,7 @@ struct fec_enet_private {
> int pause_flag;
> int wol_flag;
> u32 quirks;
> + struct gpio_desc *phy_reset;
>
> struct napi_struct napi;
> int csum_flags;
> diff --git a/drivers/net/ethernet/freescale/fec_main.c
> b/drivers/net/ethernet/freescale/fec_main.c
> index 1a983fc..7ba2bbb 100644
> --- a/drivers/net/ethernet/freescale/fec_main.c
> +++ b/drivers/net/ethernet/freescale/fec_main.c
> @@ -66,6 +66,7 @@
>
> static void set_multicast_list(struct net_device *ndev); static void
> fec_enet_itr_coal_init(struct net_device *ndev);
> +static void fec_reset_phy(struct platform_device *pdev);
>
> #define DRIVER_NAME "fec"
>
> @@ -1861,6 +1862,8 @@ static int fec_enet_clk_enable(struct net_device
> *ndev, bool enable)
> ret = clk_prepare_enable(fep->clk_enet_out);
> if (ret)
> goto failed_clk_enet_out;
> +
> + fec_reset_phy(fep->pdev);
> }
> if (fep->clk_ptp) {
> mutex_lock(&fep->ptp_clk_mutex);
> @@ -3231,13 +3234,32 @@ static int fec_enet_init(struct net_device
> *ndev) }
>
> #ifdef CONFIG_OF
> -static void fec_reset_phy(struct platform_device *pdev)
> +static struct gpio_desc *fec_get_reset_gpio(struct platform_device
> +*pdev)
> {
> struct gpio_desc *phy_reset;
> - int msec = 1;
> struct device_node *np = pdev->dev.of_node;
>
> if (!np)
> + return ERR_PTR(-ENODEV);

No need to check device node.

> +
> + phy_reset = devm_gpiod_get_optional(&pdev->dev, "phy-reset",
> + GPIOD_OUT_LOW);
> + if (IS_ERR(phy_reset))
> + dev_err(&pdev->dev, "failed to get phy-reset-gpios: %ld\n",
> + PTR_ERR(phy_reset));
> + return phy_reset;
> +}
> +
> +static void fec_reset_phy(struct platform_device *pdev) {
> + struct device_node *np = pdev->dev.of_node;
> + struct net_device *ndev = platform_get_drvdata(pdev);
> + struct fec_enet_private *fep = netdev_priv(ndev);
> + int msec = 1;
> +
> + if (!fep->phy_reset)
> + return;
> + if (!np)
> return;

No need to check device node.

>
> of_property_read_u32(np, "phy-reset-duration", &msec); @@ -3245,17
> +3267,16 @@ static void fec_reset_phy(struct platform_device *pdev)
> if (msec > 1000)
> msec = 1;
>
> - phy_reset = devm_gpiod_get_optional(&pdev->dev, "phy-reset",
> - GPIOD_OUT_LOW);
> - if (IS_ERR(phy_reset)) {
> - dev_err(&pdev->dev, "failed to get phy-reset-gpios: %ld\n",
> - PTR_ERR(phy_reset));
> - return;
> - }
> + gpiod_set_value_cansleep(fep->phy_reset, 0);
> msleep(msec);
> - gpiod_set_value_cansleep(phy_reset, 1);
> + gpiod_set_value_cansleep(fep->phy_reset, 1);
> }
> #else /* CONFIG_OF */
> +static void fec_get_reset_gpio(struct platform_device *pdev) {
> + return -EINVAL;
> +}
> +
> static void fec_reset_phy(struct platform_device *pdev) {
> /*
> @@ -3309,8 +3330,12 @@ fec_probe(struct platform_device *pdev)
> struct device_node *np = pdev->dev.of_node, *phy_node;
> int num_tx_qs;
> int num_rx_qs;
> + struct gpio_desc *phy_reset;
>
> fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
> + phy_reset = fec_get_reset_gpio(pdev);
> + if (IS_ERR(phy_reset) && PTR_ERR(phy_reset) == -EPROBE_DEFER)
> + return -EPROBE_DEFER;
>
> /* Init network device */
> ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private), @@ -
> 3331,6 +3356,7 @@ fec_probe(struct platform_device *pdev)
> fep->netdev = ndev;
> fep->num_rx_queues = num_rx_qs;
> fep->num_tx_queues = num_tx_qs;
> + fep->phy_reset = phy_reset;
>
> #if !defined(CONFIG_M5272)
> /* default enable pause frame auto negotiation */
> --
> 2.1.4