All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] convert gianfar to phylink
@ 2019-07-23 15:17 Arseny Solokha
  2019-07-23 15:17 ` [RFC PATCH 1/2] gianfar: convert " Arseny Solokha
  2019-07-23 15:17 ` [RFC PATCH 2/2] net: phylink: don't start and stop SGMII PHYs in SFP modules twice Arseny Solokha
  0 siblings, 2 replies; 26+ messages in thread
From: Arseny Solokha @ 2019-07-23 15:17 UTC (permalink / raw)
  To: Claudiu Manoil, Ioana Ciornei, Russell King, Andrew Lunn
  Cc: netdev, Arseny Solokha

The first patch in the series (almost) converts gianfar to phylink API. The
incentive behind this effort was to get proper support for 1000Base-X and
SGMII SFP modules.

There are some usages of the older phylib left, as serdes have to be
configured and its parameters queried via a TBI interface, and I've failed
to find a reasonably easy way to do it with phylink without much surgery.
It's the first reason for RFC here. However, usage of the older API only
covers two special cases of underlying hardware management and is not
involved in link and SFP management directly.

The conversion was tested with various 1000Base-X connected optical modules
and SGMII-connected copper ones.

The second patch deals with an issue in the phylink proper which only
manifests when bringing up or shutting down a network interface with SGMII
SFP module connected, which yields in calling phy_start() or phy_stop()
twice in a row for such modules. It doesn't look like a proper fix to me,
though, thus the second reason for RFC.

Arseny Solokha (2):
  gianfar: convert to phylink
  net: phylink: don't start and stop SGMII PHYs in SFP modules twice

 drivers/net/ethernet/freescale/Kconfig        |   2 +-
 drivers/net/ethernet/freescale/gianfar.c      | 409 +++++++++---------
 drivers/net/ethernet/freescale/gianfar.h      |  26 +-
 .../net/ethernet/freescale/gianfar_ethtool.c  |  79 ++--
 drivers/net/phy/phylink.c                     |   6 +-
 5 files changed, 254 insertions(+), 268 deletions(-)

-- 
2.22.0


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [RFC PATCH 1/2] gianfar: convert to phylink
  2019-07-23 15:17 [RFC PATCH 0/2] convert gianfar to phylink Arseny Solokha
@ 2019-07-23 15:17 ` Arseny Solokha
  2019-07-23 16:07   ` Claudiu Manoil
                     ` (2 more replies)
  2019-07-23 15:17 ` [RFC PATCH 2/2] net: phylink: don't start and stop SGMII PHYs in SFP modules twice Arseny Solokha
  1 sibling, 3 replies; 26+ messages in thread
From: Arseny Solokha @ 2019-07-23 15:17 UTC (permalink / raw)
  To: Claudiu Manoil, Ioana Ciornei, Russell King, Andrew Lunn
  Cc: netdev, Arseny Solokha

Convert gianfar to use the phylink API for better SFP modules support.

The driver still uses phylib for serdes configuration over the TBI
interface, as there seems to be no functionally equivalent API present
in phylink (yet). phylib usage is basically confined in two functions.

One needs to change their Device Tree accordingly to get working SFP
support:

 enet1: ethernet@25000 {
 <...>
 	device_type = "network";
 	model = "eTSEC";
 	compatible = "gianfar";
 	tbi-handle = <&tbi0>;
+	phy-connection-type = "sgmii";
+	managed = "in-band-status";
+	sfp = <&sfp>;
 	max-speed = <1000>;

 	mdio@520 {
 		#address-cells = <1>;
 		#size-cells = <0>;
 		compatible = "fsl,gianfar-tbi";
 		reg = <0x520 0x20>;

 		tbi0: tbi-phy@1f {
 			reg = <0x1f>;
 			device_type = "tbi-phy";
 		};
 	};
+
+	sfp: sfp0 {
+		compatible = "sff,sfp";
+		i2c-bus = <&i2c1>;
+		mod-def0-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
+		rate-select0-gpios = <&gpio 13 GPIO_ACTIVE_HIGH>;
+		tx-disable-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
+		tx-fault-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>;
+		los-gpios = <&gpio 3 GPIO_ACTIVE_HIGH>;
+	};
 };

Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru>
---
 drivers/net/ethernet/freescale/Kconfig        |   2 +-
 drivers/net/ethernet/freescale/gianfar.c      | 409 +++++++++---------
 drivers/net/ethernet/freescale/gianfar.h      |  26 +-
 .../net/ethernet/freescale/gianfar_ethtool.c  |  79 ++--
 4 files changed, 251 insertions(+), 265 deletions(-)

diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig
index 6a7e8993119f..8b51d423b61d 100644
--- a/drivers/net/ethernet/freescale/Kconfig
+++ b/drivers/net/ethernet/freescale/Kconfig
@@ -89,7 +89,7 @@ config GIANFAR
 	tristate "Gianfar Ethernet"
 	depends on HAS_DMA
 	select FSL_PQ_MDIO
-	select PHYLIB
+	select PHYLINK
 	select CRC32
 	---help---
 	  This driver supports the Gigabit TSEC on the MPC83xx, MPC85xx,
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 7ea19e173339..64c7b174e591 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -96,6 +96,7 @@
 #include <linux/mii.h>
 #include <linux/phy.h>
 #include <linux/phy_fixed.h>
+#include <linux/phylink.h>
 #include <linux/of.h>
 #include <linux/of_net.h>
 
@@ -117,8 +118,18 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu);
 static irqreturn_t gfar_error(int irq, void *dev_id);
 static irqreturn_t gfar_transmit(int irq, void *dev_id);
 static irqreturn_t gfar_interrupt(int irq, void *dev_id);
-static void adjust_link(struct net_device *dev);
-static noinline void gfar_update_link_state(struct gfar_private *priv);
+static void gfar_phylink_validate(struct phylink_config *config,
+				  unsigned long *supported,
+				  struct phylink_link_state *state);
+static int gfar_mac_link_state(struct phylink_config *config,
+			       struct phylink_link_state *state);
+static void gfar_mac_config(struct phylink_config *config, unsigned int mode,
+			    const struct phylink_link_state *state);
+static void gfar_mac_an_restart(struct phylink_config *config);
+static void gfar_mac_link_down(struct phylink_config *config, unsigned int mode,
+			       phy_interface_t interface);
+static void gfar_mac_link_up(struct phylink_config *config, unsigned int mode,
+			     phy_interface_t interface, struct phy_device *phy);
 static int init_phy(struct net_device *dev);
 static int gfar_probe(struct platform_device *ofdev);
 static int gfar_remove(struct platform_device *ofdev);
@@ -504,6 +515,15 @@ static const struct net_device_ops gfar_netdev_ops = {
 #endif
 };
 
+static const struct phylink_mac_ops gfar_phylink_ops = {
+	.validate = gfar_phylink_validate,
+	.mac_link_state = gfar_mac_link_state,
+	.mac_config = gfar_mac_config,
+	.mac_an_restart = gfar_mac_an_restart,
+	.mac_link_down = gfar_mac_link_down,
+	.mac_link_up = gfar_mac_link_up,
+};
+
 static void gfar_ints_disable(struct gfar_private *priv)
 {
 	int i;
@@ -733,6 +753,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
 	struct gfar_private *priv = NULL;
 	struct device_node *np = ofdev->dev.of_node;
 	struct device_node *child = NULL;
+	struct phylink *phylink;
 	u32 stash_len = 0;
 	u32 stash_idx = 0;
 	unsigned int num_tx_qs, num_rx_qs;
@@ -891,11 +912,21 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
 
 	err = of_property_read_string(np, "phy-connection-type", &ctype);
 
-	/* We only care about rgmii-id.  The rest are autodetected */
-	if (err == 0 && !strcmp(ctype, "rgmii-id"))
-		priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
-	else
+	/* We only care about rgmii-id and sgmii - the former
+	 * is indistinguishable from rgmii in hardware, and phylink needs
+	 * the latter to be set appropriately for correct phy configuration.
+	 * The rest are autodetected
+	 */
+	if (err == 0) {
+		if (!strcmp(ctype, "rgmii-id"))
+			priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
+		else if (!strcmp(ctype, "sgmii"))
+			priv->interface = PHY_INTERFACE_MODE_SGMII;
+		else
+			priv->interface = PHY_INTERFACE_MODE_MII;
+	} else {
 		priv->interface = PHY_INTERFACE_MODE_MII;
+	}
 
 	if (of_find_property(np, "fsl,magic-packet", NULL))
 		priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
@@ -903,19 +934,21 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
 	if (of_get_property(np, "fsl,wake-on-filer", NULL))
 		priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER;
 
-	priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
+	priv->device_node = np;
+	priv->speed = SPEED_UNKNOWN;
 
-	/* In the case of a fixed PHY, the DT node associated
-	 * to the PHY is the Ethernet MAC DT node.
-	 */
-	if (!priv->phy_node && of_phy_is_fixed_link(np)) {
-		err = of_phy_register_fixed_link(np);
-		if (err)
-			goto err_grp_init;
+	priv->phylink_config.dev = &priv->ndev->dev;
+	priv->phylink_config.type = PHYLINK_NETDEV;
 
-		priv->phy_node = of_node_get(np);
+	phylink = phylink_create(&priv->phylink_config, of_fwnode_handle(np),
+				 priv->interface, &gfar_phylink_ops);
+	if (IS_ERR(phylink)) {
+		err = PTR_ERR(phylink);
+		goto err_grp_init;
 	}
 
+	priv->phylink = phylink;
+
 	/* Find the TBI PHY.  If it's not there, we don't support SGMII */
 	priv->tbi_node = of_parse_phandle(np, "tbi-handle", 0);
 
@@ -994,7 +1027,7 @@ static int gfar_hwtstamp_get(struct net_device *netdev, struct ifreq *ifr)
 
 static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	struct phy_device *phydev = dev->phydev;
+	struct gfar_private *priv = netdev_priv(dev);
 
 	if (!netif_running(dev))
 		return -EINVAL;
@@ -1004,10 +1037,7 @@ static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 	if (cmd == SIOCGHWTSTAMP)
 		return gfar_hwtstamp_get(dev, rq);
 
-	if (!phydev)
-		return -ENODEV;
-
-	return phy_mii_ioctl(phydev, rq, cmd);
+	return phylink_mii_ioctl(priv->phylink, rq, cmd);
 }
 
 static u32 cluster_entry_per_class(struct gfar_private *priv, u32 rqfar,
@@ -1307,7 +1337,6 @@ static void gfar_init_addr_hash_table(struct gfar_private *priv)
  */
 static int gfar_probe(struct platform_device *ofdev)
 {
-	struct device_node *np = ofdev->dev.of_node;
 	struct net_device *dev = NULL;
 	struct gfar_private *priv = NULL;
 	int err = 0, i;
@@ -1463,12 +1492,10 @@ static int gfar_probe(struct platform_device *ofdev)
 	return 0;
 
 register_fail:
-	if (of_phy_is_fixed_link(np))
-		of_phy_deregister_fixed_link(np);
 	unmap_group_regs(priv);
 	gfar_free_rx_queues(priv);
 	gfar_free_tx_queues(priv);
-	of_node_put(priv->phy_node);
+	phylink_destroy(priv->phylink);
 	of_node_put(priv->tbi_node);
 	free_gfar_dev(priv);
 	return err;
@@ -1477,19 +1504,15 @@ static int gfar_probe(struct platform_device *ofdev)
 static int gfar_remove(struct platform_device *ofdev)
 {
 	struct gfar_private *priv = platform_get_drvdata(ofdev);
-	struct device_node *np = ofdev->dev.of_node;
 
-	of_node_put(priv->phy_node);
 	of_node_put(priv->tbi_node);
 
 	unregister_netdev(priv->ndev);
 
-	if (of_phy_is_fixed_link(np))
-		of_phy_deregister_fixed_link(np);
-
 	unmap_group_regs(priv);
 	gfar_free_rx_queues(priv);
 	gfar_free_tx_queues(priv);
+	phylink_destroy(priv->phylink);
 	free_gfar_dev(priv);
 
 	return 0;
@@ -1643,9 +1666,11 @@ static int gfar_suspend(struct device *dev)
 		gfar_start_wol_filer(priv);
 
 	} else {
-		phy_stop(ndev->phydev);
+		phylink_stop(phy->phylink);
 	}
 
+	priv->speed = SPEED_UNKNOWN;
+
 	return 0;
 }
 
@@ -1672,7 +1697,7 @@ static int gfar_resume(struct device *dev)
 		gfar_filer_restore_table(priv);
 
 	} else {
-		phy_start(ndev->phydev);
+		phylink_start(priv->phylink);
 	}
 
 	gfar_start(priv);
@@ -1702,12 +1727,7 @@ static int gfar_restore(struct device *dev)
 
 	gfar_start(priv);
 
-	priv->oldlink = 0;
-	priv->oldspeed = 0;
-	priv->oldduplex = -1;
-
-	if (ndev->phydev)
-		phy_start(ndev->phydev);
+	phylink_start(priv->phylink);
 
 	netif_device_attach(ndev);
 	enable_napi(priv);
@@ -1781,46 +1801,26 @@ static phy_interface_t gfar_get_interface(struct net_device *dev)
  */
 static int init_phy(struct net_device *dev)
 {
-	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
 	struct gfar_private *priv = netdev_priv(dev);
 	phy_interface_t interface;
-	struct phy_device *phydev;
 	struct ethtool_eee edata;
+	int flags = 0;
+	int err;
 
-	linkmode_set_bit_array(phy_10_100_features_array,
-			       ARRAY_SIZE(phy_10_100_features_array),
-			       mask);
-	linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask);
-	linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, mask);
-	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
-		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mask);
-
-	priv->oldlink = 0;
-	priv->oldspeed = 0;
-	priv->oldduplex = -1;
-
-	interface = gfar_get_interface(dev);
-
-	phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0,
-				interface);
-	if (!phydev) {
-		dev_err(&dev->dev, "could not attach to PHY\n");
-		return -ENODEV;
+	err = phylink_of_phy_connect(priv->phylink, priv->device_node, flags);
+	if (err) {
+		netdev_err(dev, "could not attach to PHY: %d\n", err);
+		return err;
 	}
 
+	priv->tbi_phy = NULL;
+	interface = gfar_get_interface(dev);
 	if (interface == PHY_INTERFACE_MODE_SGMII)
 		gfar_configure_serdes(dev);
 
-	/* Remove any features not supported by the controller */
-	linkmode_and(phydev->supported, phydev->supported, mask);
-	linkmode_copy(phydev->advertising, phydev->supported);
-
-	/* Add support for flow control */
-	phy_support_asym_pause(phydev);
-
 	/* disable EEE autoneg, EEE not supported by eTSEC */
 	memset(&edata, 0, sizeof(struct ethtool_eee));
-	phy_ethtool_set_eee(phydev, &edata);
+	phylink_ethtool_set_eee(priv->phylink, &edata);
 
 	return 0;
 }
@@ -1850,6 +1850,8 @@ static void gfar_configure_serdes(struct net_device *dev)
 		return;
 	}
 
+	priv->tbi_phy = tbiphy;
+
 	/* If the link is already up, we must already be ok, and don't need to
 	 * configure and reset the TBI<->SerDes link.  Maybe U-Boot configured
 	 * everything for us?  Resetting it takes the link down and requires
@@ -1964,7 +1966,7 @@ void stop_gfar(struct net_device *dev)
 	/* disable ints and gracefully shut down Rx/Tx DMA */
 	gfar_halt(priv);
 
-	phy_stop(dev->phydev);
+	phylink_stop(priv->phylink);
 
 	free_skb_resources(priv);
 }
@@ -2219,12 +2221,7 @@ int startup_gfar(struct net_device *ndev)
 	/* Start Rx/Tx DMA and enable the interrupts */
 	gfar_start(priv);
 
-	/* force link state update after mac reset */
-	priv->oldlink = 0;
-	priv->oldspeed = 0;
-	priv->oldduplex = -1;
-
-	phy_start(ndev->phydev);
+	phylink_start(priv->phylink);
 
 	enable_napi(priv);
 
@@ -2593,7 +2590,7 @@ static int gfar_close(struct net_device *dev)
 	stop_gfar(dev);
 
 	/* Disconnect from the PHY */
-	phy_disconnect(dev->phydev);
+	phylink_disconnect_phy(priv->phylink);
 
 	gfar_free_irq(priv);
 
@@ -3387,23 +3384,6 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id)
 	return IRQ_HANDLED;
 }
 
-/* Called every time the controller might need to be made
- * aware of new link state.  The PHY code conveys this
- * information through variables in the phydev structure, and this
- * function converts those variables into the appropriate
- * register values, and can bring down the device if needed.
- */
-static void adjust_link(struct net_device *dev)
-{
-	struct gfar_private *priv = netdev_priv(dev);
-	struct phy_device *phydev = dev->phydev;
-
-	if (unlikely(phydev->link != priv->oldlink ||
-		     (phydev->link && (phydev->duplex != priv->oldduplex ||
-				       phydev->speed != priv->oldspeed))))
-		gfar_update_link_state(priv);
-}
-
 /* Update the hash table based on the current list of multicast
  * addresses we subscribe to.  Also, change the promiscuity of
  * the device based on the flags (this function is called
@@ -3635,132 +3615,169 @@ static irqreturn_t gfar_error(int irq, void *grp_id)
 	return IRQ_HANDLED;
 }
 
-static u32 gfar_get_flowctrl_cfg(struct gfar_private *priv)
+static void gfar_phylink_validate(struct phylink_config *config,
+				  unsigned long *supported,
+				  struct phylink_link_state *state)
+{
+	struct gfar_private *priv = netdev_priv(to_net_dev(config->dev));
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+	if (state->interface != PHY_INTERFACE_MODE_NA &&
+	    state->interface != PHY_INTERFACE_MODE_SGMII &&
+	    state->interface != PHY_INTERFACE_MODE_1000BASEX &&
+	    !phy_interface_mode_is_rgmii(state->interface)) {
+		phylink_zero(supported);
+		return;
+	}
+
+	phylink_set(mask, Autoneg);
+	phylink_set(mask, Pause);
+	phylink_set(mask, Asym_Pause);
+	phylink_set_port_modes(mask);
+
+	phylink_set(mask, 10baseT_Half);
+	phylink_set(mask, 10baseT_Full);
+	phylink_set(mask, 100baseT_Half);
+	phylink_set(mask, 100baseT_Full);
+
+	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT) {
+		phylink_set(mask, 1000baseX_Full);
+		phylink_set(mask, 1000baseT_Full);
+	}
+
+	linkmode_and(supported, supported, mask);
+	linkmode_and(state->advertising, state->advertising, mask);
+}
+
+static int gfar_mac_link_state(struct phylink_config *config,
+			       struct phylink_link_state *state)
+{
+	if (state->interface == PHY_INTERFACE_MODE_SGMII ||
+	    state->interface == PHY_INTERFACE_MODE_1000BASEX) {
+		struct gfar_private *priv =
+			netdev_priv(to_net_dev(config->dev));
+		u16 tbi_cr;
+
+		if (!priv->tbi_phy)
+			return -ENODEV;
+
+		tbi_cr = phy_read(priv->tbi_phy, MII_TBI_CR);
+
+		state->duplex = !!(tbi_cr & TBI_CR_FULL_DUPLEX);
+		if ((tbi_cr & TBI_CR_SPEED_1000_MASK) == TBI_CR_SPEED_1000_MASK)
+			state->speed = SPEED_1000;
+	}
+
+	return 1;
+}
+
+static u32 gfar_get_flowctrl_cfg(const struct phylink_link_state *state)
 {
-	struct net_device *ndev = priv->ndev;
-	struct phy_device *phydev = ndev->phydev;
 	u32 val = 0;
 
-	if (!phydev->duplex)
+	if (!state->duplex)
 		return val;
 
-	if (!priv->pause_aneg_en) {
-		if (priv->tx_pause_en)
-			val |= MACCFG1_TX_FLOW;
-		if (priv->rx_pause_en)
-			val |= MACCFG1_RX_FLOW;
-	} else {
-		u16 lcl_adv, rmt_adv;
-		u8 flowctrl;
-		/* get link partner capabilities */
-		rmt_adv = 0;
-		if (phydev->pause)
-			rmt_adv = LPA_PAUSE_CAP;
-		if (phydev->asym_pause)
-			rmt_adv |= LPA_PAUSE_ASYM;
-
-		lcl_adv = linkmode_adv_to_lcl_adv_t(phydev->advertising);
-		flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
-		if (flowctrl & FLOW_CTRL_TX)
-			val |= MACCFG1_TX_FLOW;
-		if (flowctrl & FLOW_CTRL_RX)
-			val |= MACCFG1_RX_FLOW;
-	}
+	if (state->pause & MLO_PAUSE_TX)
+		val |= MACCFG1_TX_FLOW;
+	if (state->pause & MLO_PAUSE_RX)
+		val |= MACCFG1_RX_FLOW;
 
 	return val;
 }
 
-static noinline void gfar_update_link_state(struct gfar_private *priv)
+static void gfar_mac_config(struct phylink_config *config, unsigned int mode,
+			    const struct phylink_link_state *state)
 {
+	struct gfar_private *priv = netdev_priv(to_net_dev(config->dev));
 	struct gfar __iomem *regs = priv->gfargrp[0].regs;
-	struct net_device *ndev = priv->ndev;
-	struct phy_device *phydev = ndev->phydev;
-	struct gfar_priv_rx_q *rx_queue = NULL;
-	int i;
+	u32 maccfg1, new_maccfg1;
+	u32 maccfg2, new_maccfg2;
+	u32 ecntrl, new_ecntrl;
+	u32 tx_flow, new_tx_flow;
 
 	if (unlikely(test_bit(GFAR_RESETTING, &priv->state)))
 		return;
 
-	if (phydev->link) {
-		u32 tempval1 = gfar_read(&regs->maccfg1);
-		u32 tempval = gfar_read(&regs->maccfg2);
-		u32 ecntrl = gfar_read(&regs->ecntrl);
-		u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW);
+	if (unlikely(phylink_autoneg_inband(mode)))
+		return;
 
-		if (phydev->duplex != priv->oldduplex) {
-			if (!(phydev->duplex))
-				tempval &= ~(MACCFG2_FULL_DUPLEX);
-			else
-				tempval |= MACCFG2_FULL_DUPLEX;
+	maccfg1 = gfar_read(&regs->maccfg1);
+	maccfg2 = gfar_read(&regs->maccfg2);
+	ecntrl = gfar_read(&regs->ecntrl);
 
-			priv->oldduplex = phydev->duplex;
-		}
+	new_maccfg2 = maccfg2 & ~(MACCFG2_FULL_DUPLEX | MACCFG2_IF);
+	new_ecntrl = ecntrl & ~ECNTRL_R100;
 
-		if (phydev->speed != priv->oldspeed) {
-			switch (phydev->speed) {
-			case 1000:
-				tempval =
-				    ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
+	if (state->duplex)
+		new_maccfg2 |= MACCFG2_FULL_DUPLEX;
 
-				ecntrl &= ~(ECNTRL_R100);
-				break;
-			case 100:
-			case 10:
-				tempval =
-				    ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
-
-				/* Reduced mode distinguishes
-				 * between 10 and 100
-				 */
-				if (phydev->speed == SPEED_100)
-					ecntrl |= ECNTRL_R100;
-				else
-					ecntrl &= ~(ECNTRL_R100);
-				break;
-			default:
-				netif_warn(priv, link, priv->ndev,
-					   "Ack!  Speed (%d) is not 10/100/1000!\n",
-					   phydev->speed);
-				break;
-			}
-
-			priv->oldspeed = phydev->speed;
-		}
-
-		tempval1 &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
-		tempval1 |= gfar_get_flowctrl_cfg(priv);
-
-		/* Turn last free buffer recording on */
-		if ((tempval1 & MACCFG1_TX_FLOW) && !tx_flow_oldval) {
-			for (i = 0; i < priv->num_rx_queues; i++) {
-				u32 bdp_dma;
-
-				rx_queue = priv->rx_queue[i];
-				bdp_dma = gfar_rxbd_dma_lastfree(rx_queue);
-				gfar_write(rx_queue->rfbptr, bdp_dma);
-			}
-
-			priv->tx_actual_en = 1;
-		}
-
-		if (unlikely(!(tempval1 & MACCFG1_TX_FLOW) && tx_flow_oldval))
-			priv->tx_actual_en = 0;
-
-		gfar_write(&regs->maccfg1, tempval1);
-		gfar_write(&regs->maccfg2, tempval);
-		gfar_write(&regs->ecntrl, ecntrl);
-
-		if (!priv->oldlink)
-			priv->oldlink = 1;
-
-	} else if (priv->oldlink) {
-		priv->oldlink = 0;
-		priv->oldspeed = 0;
-		priv->oldduplex = -1;
+	switch (state->speed) {
+	case SPEED_1000:
+		new_maccfg2 |= MACCFG2_GMII;
+		break;
+	case SPEED_100:
+		new_maccfg2 |= MACCFG2_MII;
+		new_ecntrl = ecntrl | ECNTRL_R100;
+		break;
+	case SPEED_10:
+		new_maccfg2 |= MACCFG2_MII;
+		break;
+	default:
+		netif_warn(priv, link, priv->ndev,
+			   "Ack!  Speed (%d) is not 10/100/1000!\n",
+			   state->speed);
+		return;
 	}
 
-	if (netif_msg_link(priv))
-		phy_print_status(phydev);
+	priv->speed = state->speed;
+
+	new_maccfg1 = maccfg1 & ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
+	new_maccfg1 |= gfar_get_flowctrl_cfg(state);
+
+	/* Turn last free buffer recording on */
+	tx_flow = maccfg1 & MACCFG1_TX_FLOW;
+	new_tx_flow = new_maccfg1 & MACCFG1_TX_FLOW;
+	if (new_tx_flow && !tx_flow) {
+		int i;
+
+		for (i = 0; i < priv->num_rx_queues; i++) {
+			struct gfar_priv_rx_q *rx_queue;
+			u32 bdp_dma;
+
+			rx_queue = priv->rx_queue[i];
+			bdp_dma = gfar_rxbd_dma_lastfree(rx_queue);
+			gfar_write(rx_queue->rfbptr, bdp_dma);
+		}
+
+		priv->tx_actual_en = 1;
+	} else if (unlikely(!new_tx_flow && tx_flow)) {
+		priv->tx_actual_en = 0;
+	}
+
+	if (new_maccfg1 != maccfg1)
+		gfar_write(&regs->maccfg1, new_maccfg1);
+	if (new_maccfg2 != maccfg2)
+		gfar_write(&regs->maccfg2, new_maccfg2);
+	if (new_ecntrl != ecntrl)
+		gfar_write(&regs->ecntrl, new_ecntrl);
+}
+
+static void gfar_mac_an_restart(struct phylink_config *config)
+{
+	/* Not supported */
+}
+
+static void gfar_mac_link_down(struct phylink_config *config, unsigned int mode,
+			       phy_interface_t interface)
+{
+	/* Not supported */
+}
+
+static void gfar_mac_link_up(struct phylink_config *config, unsigned int mode,
+			     phy_interface_t interface, struct phy_device *phy)
+{
+	/* Not supported */
 }
 
 static const struct of_device_id gfar_match[] =
diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h
index f2af96349c7b..0b28b1f60f2d 100644
--- a/drivers/net/ethernet/freescale/gianfar.h
+++ b/drivers/net/ethernet/freescale/gianfar.h
@@ -31,8 +31,7 @@
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
 #include <linux/mm.h>
-#include <linux/mii.h>
-#include <linux/phy.h>
+#include <linux/phylink.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -149,8 +148,13 @@ extern const char gfar_driver_version[];
 #define GFAR_SUPPORTED_GBIT SUPPORTED_1000baseT_Full
 
 /* TBI register addresses */
+#define MII_TBI_CR		0x00
 #define MII_TBICON		0x11
 
+/* TBI_CR register bit fields */
+#define TBI_CR_FULL_DUPLEX	0x0100
+#define TBI_CR_SPEED_1000_MASK	0x0040
+
 /* TBICON register bit fields */
 #define TBICON_CLK_SELECT	0x0020
 
@@ -1148,12 +1152,12 @@ struct gfar_private {
 
 	/* PHY stuff */
 	phy_interface_t interface;
-	struct device_node *phy_node;
+	struct device_node *device_node;
 	struct device_node *tbi_node;
-	struct mii_bus *mii_bus;
-	int oldspeed;
-	int oldduplex;
-	int oldlink;
+	struct phylink *phylink;
+	struct phylink_config phylink_config;
+	struct phy_device *tbi_phy;
+	int speed;
 
 	uint32_t msg_enable;
 
@@ -1165,11 +1169,7 @@ struct gfar_private {
 		bd_stash_en:1,
 		rx_filer_enable:1,
 		/* Enable priorty based Tx scheduling in Hw */
-		prio_sched_en:1,
-		/* Flow control flags */
-		pause_aneg_en:1,
-		tx_pause_en:1,
-		rx_pause_en:1;
+		prio_sched_en:1;
 
 	/* The total tx and rx ring size for the enabled queues */
 	unsigned int total_tx_ring_size;
@@ -1333,8 +1333,6 @@ void reset_gfar(struct net_device *dev);
 void gfar_mac_reset(struct gfar_private *priv);
 void gfar_halt(struct gfar_private *priv);
 void gfar_start(struct gfar_private *priv);
-void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable,
-		   u32 regnum, u32 read);
 void gfar_configure_coalescing_all(struct gfar_private *priv);
 int gfar_set_features(struct net_device *dev, netdev_features_t features);
 
diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
index 3433b46b90c1..146b30d07789 100644
--- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
+++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
@@ -35,7 +35,7 @@
 #include <asm/types.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
-#include <linux/phy.h>
+#include <linux/phylink.h>
 #include <linux/sort.h>
 #include <linux/if_vlan.h>
 #include <linux/of_platform.h>
@@ -207,12 +207,10 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs,
 static unsigned int gfar_usecs2ticks(struct gfar_private *priv,
 				     unsigned int usecs)
 {
-	struct net_device *ndev = priv->ndev;
-	struct phy_device *phydev = ndev->phydev;
 	unsigned int count;
 
 	/* The timer is different, depending on the interface speed */
-	switch (phydev->speed) {
+	switch (priv->speed) {
 	case SPEED_1000:
 		count = GFAR_GBIT_TIME;
 		break;
@@ -234,12 +232,10 @@ static unsigned int gfar_usecs2ticks(struct gfar_private *priv,
 static unsigned int gfar_ticks2usecs(struct gfar_private *priv,
 				     unsigned int ticks)
 {
-	struct net_device *ndev = priv->ndev;
-	struct phy_device *phydev = ndev->phydev;
 	unsigned int count;
 
 	/* The timer is different, depending on the interface speed */
-	switch (phydev->speed) {
+	switch (priv->speed) {
 	case SPEED_1000:
 		count = GFAR_GBIT_TIME;
 		break;
@@ -489,58 +485,15 @@ static void gfar_gpauseparam(struct net_device *dev,
 {
 	struct gfar_private *priv = netdev_priv(dev);
 
-	epause->autoneg = !!priv->pause_aneg_en;
-	epause->rx_pause = !!priv->rx_pause_en;
-	epause->tx_pause = !!priv->tx_pause_en;
+	phylink_ethtool_get_pauseparam(priv->phylink, epause);
 }
 
 static int gfar_spauseparam(struct net_device *dev,
 			    struct ethtool_pauseparam *epause)
 {
 	struct gfar_private *priv = netdev_priv(dev);
-	struct phy_device *phydev = dev->phydev;
-	struct gfar __iomem *regs = priv->gfargrp[0].regs;
 
-	if (!phydev)
-		return -ENODEV;
-
-	if (!phy_validate_pause(phydev, epause))
-		return -EINVAL;
-
-	priv->rx_pause_en = priv->tx_pause_en = 0;
-	phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause);
-	if (epause->rx_pause) {
-		priv->rx_pause_en = 1;
-
-		if (epause->tx_pause) {
-			priv->tx_pause_en = 1;
-		}
-	} else if (epause->tx_pause) {
-		priv->tx_pause_en = 1;
-	}
-
-	if (epause->autoneg)
-		priv->pause_aneg_en = 1;
-	else
-		priv->pause_aneg_en = 0;
-
-	if (!epause->autoneg) {
-		u32 tempval = gfar_read(&regs->maccfg1);
-
-		tempval &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
-
-		priv->tx_actual_en = 0;
-		if (priv->tx_pause_en) {
-			priv->tx_actual_en = 1;
-			tempval |= MACCFG1_TX_FLOW;
-		}
-
-		if (priv->rx_pause_en)
-			tempval |= MACCFG1_RX_FLOW;
-		gfar_write(&regs->maccfg1, tempval);
-	}
-
-	return 0;
+	return phylink_ethtool_set_pauseparam(priv->phylink, epause);
 }
 
 int gfar_set_features(struct net_device *dev, netdev_features_t features)
@@ -1519,6 +1472,24 @@ static int gfar_get_ts_info(struct net_device *dev,
 	return 0;
 }
 
+/* Set link ksettings (phy address, speed) for ethtools */
+static int gfar_get_link_ksettings(struct net_device *dev,
+				   struct ethtool_link_ksettings *cmd)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+
+	return phylink_ethtool_ksettings_get(priv->phylink, cmd);
+}
+
+/* Get link ksettings for ethtools */
+static int gfar_set_link_ksettings(struct net_device *dev,
+				   const struct ethtool_link_ksettings *cmd)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+
+	return phylink_ethtool_ksettings_set(priv->phylink, cmd);
+}
+
 const struct ethtool_ops gfar_ethtool_ops = {
 	.get_drvinfo = gfar_gdrvinfo,
 	.get_regs_len = gfar_reglen,
@@ -1542,6 +1513,6 @@ const struct ethtool_ops gfar_ethtool_ops = {
 	.set_rxnfc = gfar_set_nfc,
 	.get_rxnfc = gfar_get_nfc,
 	.get_ts_info = gfar_get_ts_info,
-	.get_link_ksettings = phy_ethtool_get_link_ksettings,
-	.set_link_ksettings = phy_ethtool_set_link_ksettings,
+	.get_link_ksettings = gfar_get_link_ksettings,
+	.set_link_ksettings = gfar_set_link_ksettings,
 };
-- 
2.22.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC PATCH 2/2] net: phylink: don't start and stop SGMII PHYs in SFP modules twice
  2019-07-23 15:17 [RFC PATCH 0/2] convert gianfar to phylink Arseny Solokha
  2019-07-23 15:17 ` [RFC PATCH 1/2] gianfar: convert " Arseny Solokha
@ 2019-07-23 15:17 ` Arseny Solokha
  2019-07-24  9:01   ` Russell King - ARM Linux admin
  1 sibling, 1 reply; 26+ messages in thread
From: Arseny Solokha @ 2019-07-23 15:17 UTC (permalink / raw)
  To: Claudiu Manoil, Ioana Ciornei, Russell King, Andrew Lunn
  Cc: netdev, Arseny Solokha

SFP modules connected using the SGMII interface have their own PHYs which
are handled by the struct phylink's phydev field. After commit ce0aa27ff3f6
("sfp: add sfp-bus to bridge between network devices and sfp cages") an
sfp-bus attached to the same phylink also gets control over a PHY in an SFP
module which is actually the same PHY managed by phylink itself. This
results in WARNs during network interface bringup and shutdown when a
copper SFP module is connected, as phy_start() and phy_stop() are called
twice in a row for the same phy_device:

  % ip link set up dev eth0
  ------------[ cut here ]------------
  called from state UP
  WARNING: CPU: 1 PID: 155 at drivers/net/phy/phy.c:895 phy_start+0x74/0xc0
  Modules linked in:
  CPU: 1 PID: 155 Comm: backend Not tainted 5.2.0+ #1
  NIP:  c0227bf0 LR: c0227bf0 CTR: c004d224
  REGS: df547720 TRAP: 0700   Not tainted  (5.2.0+)
  MSR:  00029000 <CE,EE,ME>  CR: 24002822  XER: 00000000

  GPR00: c0227bf0 df5477d8 df5d7080 00000014 df9d2370 df9d5ac4 1f4eb000 00000001
  GPR08: c061fe58 00000000 00000000 df5477d8 0000003c 100c8768 00000000 00000000
  GPR16: df486a00 c046f1c8 c046eea0 00000000 c046e904 c0239604 db68449c 00000000
  GPR24: e9083204 00000000 00000001 db684460 e9083404 00000000 db6dce00 db6dcc00
  NIP [c0227bf0] phy_start+0x74/0xc0
  LR [c0227bf0] phy_start+0x74/0xc0
  Call Trace:
  [df5477d8] [c0227bf0] phy_start+0x74/0xc0 (unreliable)
  [df5477e8] [c023cad0] startup_gfar+0x398/0x3f4
  [df547828] [c023cf08] gfar_enet_open+0x364/0x374
  [df547898] [c029d870] __dev_open+0xe4/0x140
  [df5478c8] [c029db70] __dev_change_flags+0xf0/0x188
  [df5478f8] [c029dc28] dev_change_flags+0x20/0x54
  [df547918] [c02ae304] do_setlink+0x310/0x818
  [df547a08] [c02b1eb8] __rtnl_newlink+0x384/0x6b0
  [df547c28] [c02b222c] rtnl_newlink+0x48/0x68
  [df547c48] [c02ad7c8] rtnetlink_rcv_msg+0x240/0x27c
  [df547c98] [c02cc068] netlink_rcv_skb+0x8c/0xf0
  [df547cd8] [c02cba3c] netlink_unicast+0x114/0x19c
  [df547d08] [c02cbd74] netlink_sendmsg+0x2b0/0x2c0
  [df547d58] [c027b668] sock_sendmsg_nosec+0x20/0x40
  [df547d68] [c027d080] ___sys_sendmsg+0x17c/0x1dc
  [df547e98] [c027df7c] __sys_sendmsg+0x68/0x84
  [df547ef8] [c027e430] sys_socketcall+0x1a0/0x204
  [df547f38] [c000d1d8] ret_from_syscall+0x0/0x38
  --- interrupt: c01 at 0xfd4e030
      LR = 0xfd4e010
  Instruction dump:
  813f0188 38800000 2b890005 419d0014 3d40c046 5529103a 394aa208 7c8a482e
  3c60c046 3863a1b8 4cc63182 4be009a1 <0fe00000> 48000030 3c60c046 3863a1d0
  ---[ end trace d4c095aeaf6ea998 ]---

and

  % ip link set down dev eth0
  ------------[ cut here ]------------
  called from state HALTED
  WARNING: CPU: 1 PID: 184 at drivers/net/phy/phy.c:858 phy_stop+0x3c/0x88

  <...>

  Call Trace:
  [df581788] [c0228450] phy_stop+0x3c/0x88 (unreliable)
  [df581798] [c022d548] sfp_sm_phy_detach+0x1c/0x44
  [df5817a8] [c022e8cc] sfp_sm_event+0x4b0/0x87c
  [df581848] [c022f04c] sfp_upstream_stop+0x34/0x44
  [df581858] [c0225608] phylink_stop+0x7c/0xe4
  [df581868] [c023c57c] stop_gfar+0x7c/0x94
  [df581888] [c023c5b8] gfar_close+0x24/0x94
  [df5818a8] [c0298688] __dev_close_many+0xdc/0xf8
  [df5818c8] [c029db58] __dev_change_flags+0xd8/0x188
  [df5818f8] [c029dc28] dev_change_flags+0x20/0x54
  [df581918] [c02ae304] do_setlink+0x310/0x818
  [df581a08] [c02b1eb8] __rtnl_newlink+0x384/0x6b0
  [df581c28] [c02b222c] rtnl_newlink+0x48/0x68
  [df581c48] [c02ad7c8] rtnetlink_rcv_msg+0x240/0x27c
  [df581c98] [c02cc068] netlink_rcv_skb+0x8c/0xf0
  [df581cd8] [c02cba3c] netlink_unicast+0x114/0x19c
  [df581d08] [c02cbd74] netlink_sendmsg+0x2b0/0x2c0
  [df581d58] [c027b668] sock_sendmsg_nosec+0x20/0x40
  [df581d68] [c027d080] ___sys_sendmsg+0x17c/0x1dc
  [df581e98] [c027df7c] __sys_sendmsg+0x68/0x84
  [df581ef8] [c027e430] sys_socketcall+0x1a0/0x204
  [df581f38] [c000d1d8] ret_from_syscall+0x0/0x38

  <...>

  ---[ end trace d4c095aeaf6ea999 ]---

SFP modules with the 1000Base-X interface are not affected.

So, skip explicit calls to phy_start() and phy_stop() when phylink has just
enabled or disabled an attached SFP module.

Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru>
---
 drivers/net/phy/phylink.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 5d0af041b8f9..4de7665876af 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -992,7 +992,7 @@ void phylink_start(struct phylink *pl)
 		mod_timer(&pl->link_poll, jiffies + HZ);
 	if (pl->sfp_bus)
 		sfp_upstream_start(pl->sfp_bus);
-	if (pl->phydev)
+	else if (pl->phydev)
 		phy_start(pl->phydev);
 }
 EXPORT_SYMBOL_GPL(phylink_start);
@@ -1010,10 +1010,10 @@ void phylink_stop(struct phylink *pl)
 {
 	ASSERT_RTNL();
 
-	if (pl->phydev)
-		phy_stop(pl->phydev);
 	if (pl->sfp_bus)
 		sfp_upstream_stop(pl->sfp_bus);
+	else if (pl->phydev)
+		phy_stop(pl->phydev);
 	del_timer_sync(&pl->link_poll);
 	if (pl->link_irq) {
 		free_irq(pl->link_irq, pl);
-- 
2.22.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* RE: [RFC PATCH 1/2] gianfar: convert to phylink
  2019-07-23 15:17 ` [RFC PATCH 1/2] gianfar: convert " Arseny Solokha
@ 2019-07-23 16:07   ` Claudiu Manoil
  2019-07-24  7:36     ` Arseny Solokha
  2019-07-24  8:19   ` Russell King - ARM Linux admin
  2019-07-29 23:39   ` Vladimir Oltean
  2 siblings, 1 reply; 26+ messages in thread
From: Claudiu Manoil @ 2019-07-23 16:07 UTC (permalink / raw)
  To: Arseny Solokha, Ioana Ciornei, Russell King, Andrew Lunn; +Cc: netdev

>-----Original Message-----
>From: Arseny Solokha <asolokha@kb.kras.ru>
>Sent: Tuesday, July 23, 2019 6:17 PM
>To: Claudiu Manoil <claudiu.manoil@nxp.com>; Ioana Ciornei
><ioana.ciornei@nxp.com>; Russell King <linux@armlinux.org.uk>; Andrew Lunn
><andrew@lunn.ch>
>Cc: netdev@vger.kernel.org; Arseny Solokha <asolokha@kb.kras.ru>
>Subject: [RFC PATCH 1/2] gianfar: convert to phylink
>
>Convert gianfar to use the phylink API for better SFP modules support.
>
>The driver still uses phylib for serdes configuration over the TBI
>interface, as there seems to be no functionally equivalent API present
>in phylink (yet). phylib usage is basically confined in two functions.
>

Thanks for your patch.  Phylink in gianfar... that would be something!
At first glance a lot of code has changed with this patch or got relocated.
To make it easier to swallow, I think a few cleanup patches could be
separated before migrating to phylink.  Like for instance getting rid of the
old* link state variables, which I think are an artifact from early phylib usage.
Nonetheless good to see this implemented, I'll have a closer look asap.

Thanks
Claudiu

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/2] gianfar: convert to phylink
  2019-07-23 16:07   ` Claudiu Manoil
@ 2019-07-24  7:36     ` Arseny Solokha
  0 siblings, 0 replies; 26+ messages in thread
From: Arseny Solokha @ 2019-07-24  7:36 UTC (permalink / raw)
  To: Claudiu Manoil; +Cc: Ioana Ciornei, Russell King, Andrew Lunn, netdev

>>-----Original Message-----
>>From: Arseny Solokha <asolokha@kb.kras.ru>
>>Sent: Tuesday, July 23, 2019 6:17 PM
>>To: Claudiu Manoil <claudiu.manoil@nxp.com>; Ioana Ciornei
>><ioana.ciornei@nxp.com>; Russell King <linux@armlinux.org.uk>; Andrew Lunn
>><andrew@lunn.ch>
>>Cc: netdev@vger.kernel.org; Arseny Solokha <asolokha@kb.kras.ru>
>>Subject: [RFC PATCH 1/2] gianfar: convert to phylink
>>
>>Convert gianfar to use the phylink API for better SFP modules support.
>>
>>The driver still uses phylib for serdes configuration over the TBI
>>interface, as there seems to be no functionally equivalent API present
>>in phylink (yet). phylib usage is basically confined in two functions.
>>
>
> Thanks for your patch.  Phylink in gianfar... that would be something!
> At first glance a lot of code has changed with this patch or got relocated.
> To make it easier to swallow, I think a few cleanup patches could be
> separated before migrating to phylink.  Like for instance getting rid of the
> old* link state variables, which I think are an artifact from early phylib usage.
> Nonetheless good to see this implemented, I'll have a closer look asap.

Hi,

meanwhile I'll have to post v2 of this patch because it has some issues which
initially escaped my attention. For now I'm pasting the diff against v1 here for
reference:

--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -1666,7 +1666,7 @@ static int gfar_suspend(struct device *dev)
 		gfar_start_wol_filer(priv);
 
 	} else {
-		phylink_stop(phy->phylink);
+		phylink_stop(priv->phylink);
 	}
 
 	priv->speed = SPEED_UNKNOWN;
@@ -3699,9 +3699,6 @@ static void gfar_mac_config(struct phylink_config *config, unsigned int mode,
 	if (unlikely(test_bit(GFAR_RESETTING, &priv->state)))
 		return;
 
-	if (unlikely(phylink_autoneg_inband(mode)))
-		return;
-
 	maccfg1 = gfar_read(&regs->maccfg1);
 	maccfg2 = gfar_read(&regs->maccfg2);
 	ecntrl = gfar_read(&regs->ecntrl);

The first hunk here fixes a typo which broke build with PM enabled. The second
one removes an early return from gfar_mac_config() which I believe is really
bogus and also breaks coalesce parameters calculation for SGMII and 1000Base-X
attached PHYs.

I'd like to submit a real v2 after the patches gets actual review, though.

Thanks,
Arseny

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/2] gianfar: convert to phylink
  2019-07-23 15:17 ` [RFC PATCH 1/2] gianfar: convert " Arseny Solokha
  2019-07-23 16:07   ` Claudiu Manoil
@ 2019-07-24  8:19   ` Russell King - ARM Linux admin
  2019-07-29 23:39   ` Vladimir Oltean
  2 siblings, 0 replies; 26+ messages in thread
From: Russell King - ARM Linux admin @ 2019-07-24  8:19 UTC (permalink / raw)
  To: Arseny Solokha; +Cc: Claudiu Manoil, Ioana Ciornei, Andrew Lunn, netdev

On Tue, Jul 23, 2019 at 10:17:01PM +0700, Arseny Solokha wrote:
> -static noinline void gfar_update_link_state(struct gfar_private *priv)
> +static void gfar_mac_config(struct phylink_config *config, unsigned int mode,
> +			    const struct phylink_link_state *state)
>  {
> +	struct gfar_private *priv = netdev_priv(to_net_dev(config->dev));
>  	struct gfar __iomem *regs = priv->gfargrp[0].regs;
> -	struct net_device *ndev = priv->ndev;
> -	struct phy_device *phydev = ndev->phydev;
> -	struct gfar_priv_rx_q *rx_queue = NULL;
> -	int i;
> +	u32 maccfg1, new_maccfg1;
> +	u32 maccfg2, new_maccfg2;
> +	u32 ecntrl, new_ecntrl;
> +	u32 tx_flow, new_tx_flow;
>  
>  	if (unlikely(test_bit(GFAR_RESETTING, &priv->state)))
>  		return;
>  
> -	if (phydev->link) {
> -		u32 tempval1 = gfar_read(&regs->maccfg1);
> -		u32 tempval = gfar_read(&regs->maccfg2);
> -		u32 ecntrl = gfar_read(&regs->ecntrl);
> -		u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW);
> +	if (unlikely(phylink_autoneg_inband(mode)))
> +		return;

Given that SFPs can be either SGMII or 1000BASE-X (which require
different configuration) and that the intention here is to support
SFPs, I don't see how this works with the above.

How is the difference between SGMII and 1000BASE-X handled?

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
According to speedtest.net: 11.9Mbps down 500kbps up

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 2/2] net: phylink: don't start and stop SGMII PHYs in SFP modules twice
  2019-07-23 15:17 ` [RFC PATCH 2/2] net: phylink: don't start and stop SGMII PHYs in SFP modules twice Arseny Solokha
@ 2019-07-24  9:01   ` Russell King - ARM Linux admin
  2019-07-24 13:31     ` [PATCH v2] " Arseny Solokha
  0 siblings, 1 reply; 26+ messages in thread
From: Russell King - ARM Linux admin @ 2019-07-24  9:01 UTC (permalink / raw)
  To: Arseny Solokha; +Cc: Claudiu Manoil, Ioana Ciornei, Andrew Lunn, netdev

On Tue, Jul 23, 2019 at 10:17:02PM +0700, Arseny Solokha wrote:
> SFP modules connected using the SGMII interface have their own PHYs which
> are handled by the struct phylink's phydev field. After commit ce0aa27ff3f6
> ("sfp: add sfp-bus to bridge between network devices and sfp cages") an
> sfp-bus attached to the same phylink also gets control over a PHY in an SFP
> module which is actually the same PHY managed by phylink itself. This
> results in WARNs during network interface bringup and shutdown when a
> copper SFP module is connected, as phy_start() and phy_stop() are called
> twice in a row for the same phy_device:
>...
> So, skip explicit calls to phy_start() and phy_stop() when phylink has just
> enabled or disabled an attached SFP module.

I'd prefer if we re-ordered these so phy_start() happens before
sfp_upstream_start() and the reverse for the stop calls.

pl->phydev won't be set at these points, so the calls will be no-ops.
(The reason is when we support mac--phy--sfp setups, having the
phy_start() and phy_stop() here are still necessary.)

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
According to speedtest.net: 11.9Mbps down 500kbps up

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH v2] net: phylink: don't start and stop SGMII PHYs in SFP modules twice
  2019-07-24  9:01   ` Russell King - ARM Linux admin
@ 2019-07-24 13:31     ` Arseny Solokha
  2019-07-24 13:36       ` Andrew Lunn
                         ` (2 more replies)
  0 siblings, 3 replies; 26+ messages in thread
From: Arseny Solokha @ 2019-07-24 13:31 UTC (permalink / raw)
  To: Claudiu Manoil, Ioana Ciornei, Russell King, Andrew Lunn
  Cc: netdev, Arseny Solokha

SFP modules connected using the SGMII interface have their own PHYs which
are handled by the struct phylink's phydev field. On the other hand, for
the modules connected using 1000Base-X interface that field is not set.

Since commit ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network
devices and sfp cages") phylink_start() ends up setting the phydev field
using the sfp-bus infrastructure, which eventually calls phy_start() on it,
and then calling phy_start() again on the same phydev from phylink_start()
itself. Similar call sequence holds for phylink_stop(), only in the reverse
order. This results in WARNs during network interface bringup and shutdown
when a copper SFP module is connected, as phy_start() and phy_stop() are
called twice in a row for the same phy_device:

  % ip link set up dev eth0
  ------------[ cut here ]------------
  called from state UP
  WARNING: CPU: 1 PID: 155 at drivers/net/phy/phy.c:895 phy_start+0x74/0xc0
  Modules linked in:
  CPU: 1 PID: 155 Comm: backend Not tainted 5.2.0+ #1
  NIP:  c0227bf0 LR: c0227bf0 CTR: c004d224
  REGS: df547720 TRAP: 0700   Not tainted  (5.2.0+)
  MSR:  00029000 <CE,EE,ME>  CR: 24002822  XER: 00000000

  GPR00: c0227bf0 df5477d8 df5d7080 00000014 df9d2370 df9d5ac4 1f4eb000 00000001
  GPR08: c061fe58 00000000 00000000 df5477d8 0000003c 100c8768 00000000 00000000
  GPR16: df486a00 c046f1c8 c046eea0 00000000 c046e904 c0239604 db68449c 00000000
  GPR24: e9083204 00000000 00000001 db684460 e9083404 00000000 db6dce00 db6dcc00
  NIP [c0227bf0] phy_start+0x74/0xc0
  LR [c0227bf0] phy_start+0x74/0xc0
  Call Trace:
  [df5477d8] [c0227bf0] phy_start+0x74/0xc0 (unreliable)
  [df5477e8] [c023cad0] startup_gfar+0x398/0x3f4
  [df547828] [c023cf08] gfar_enet_open+0x364/0x374
  [df547898] [c029d870] __dev_open+0xe4/0x140
  [df5478c8] [c029db70] __dev_change_flags+0xf0/0x188
  [df5478f8] [c029dc28] dev_change_flags+0x20/0x54
  [df547918] [c02ae304] do_setlink+0x310/0x818
  [df547a08] [c02b1eb8] __rtnl_newlink+0x384/0x6b0
  [df547c28] [c02b222c] rtnl_newlink+0x48/0x68
  [df547c48] [c02ad7c8] rtnetlink_rcv_msg+0x240/0x27c
  [df547c98] [c02cc068] netlink_rcv_skb+0x8c/0xf0
  [df547cd8] [c02cba3c] netlink_unicast+0x114/0x19c
  [df547d08] [c02cbd74] netlink_sendmsg+0x2b0/0x2c0
  [df547d58] [c027b668] sock_sendmsg_nosec+0x20/0x40
  [df547d68] [c027d080] ___sys_sendmsg+0x17c/0x1dc
  [df547e98] [c027df7c] __sys_sendmsg+0x68/0x84
  [df547ef8] [c027e430] sys_socketcall+0x1a0/0x204
  [df547f38] [c000d1d8] ret_from_syscall+0x0/0x38
  --- interrupt: c01 at 0xfd4e030
      LR = 0xfd4e010
  Instruction dump:
  813f0188 38800000 2b890005 419d0014 3d40c046 5529103a 394aa208 7c8a482e
  3c60c046 3863a1b8 4cc63182 4be009a1 <0fe00000> 48000030 3c60c046 3863a1d0
  ---[ end trace d4c095aeaf6ea998 ]---

and

  % ip link set down dev eth0
  ------------[ cut here ]------------
  called from state HALTED
  WARNING: CPU: 1 PID: 184 at drivers/net/phy/phy.c:858 phy_stop+0x3c/0x88

  <...>

  Call Trace:
  [df581788] [c0228450] phy_stop+0x3c/0x88 (unreliable)
  [df581798] [c022d548] sfp_sm_phy_detach+0x1c/0x44
  [df5817a8] [c022e8cc] sfp_sm_event+0x4b0/0x87c
  [df581848] [c022f04c] sfp_upstream_stop+0x34/0x44
  [df581858] [c0225608] phylink_stop+0x7c/0xe4
  [df581868] [c023c57c] stop_gfar+0x7c/0x94
  [df581888] [c023c5b8] gfar_close+0x24/0x94
  [df5818a8] [c0298688] __dev_close_many+0xdc/0xf8
  [df5818c8] [c029db58] __dev_change_flags+0xd8/0x188
  [df5818f8] [c029dc28] dev_change_flags+0x20/0x54
  [df581918] [c02ae304] do_setlink+0x310/0x818
  [df581a08] [c02b1eb8] __rtnl_newlink+0x384/0x6b0
  [df581c28] [c02b222c] rtnl_newlink+0x48/0x68
  [df581c48] [c02ad7c8] rtnetlink_rcv_msg+0x240/0x27c
  [df581c98] [c02cc068] netlink_rcv_skb+0x8c/0xf0
  [df581cd8] [c02cba3c] netlink_unicast+0x114/0x19c
  [df581d08] [c02cbd74] netlink_sendmsg+0x2b0/0x2c0
  [df581d58] [c027b668] sock_sendmsg_nosec+0x20/0x40
  [df581d68] [c027d080] ___sys_sendmsg+0x17c/0x1dc
  [df581e98] [c027df7c] __sys_sendmsg+0x68/0x84
  [df581ef8] [c027e430] sys_socketcall+0x1a0/0x204
  [df581f38] [c000d1d8] ret_from_syscall+0x0/0x38

  <...>

  ---[ end trace d4c095aeaf6ea999 ]---

SFP modules with the 1000Base-X interface are not affected.

Place explicit calls to phy_start() and phy_stop() before enabling or after
disabling an attached SFP module, where phydev is not yet set (or is
already unset), so they will be made only from the inside of sfp-bus, if
needed.

Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru>
---
Changes in v2:
 - Moved phy_start() before sfp_upstream_start(), and phy_stop() after
 sfp_upstream_stop(), and reworded the commit message accordingly.

This is a general fix and may be taken out from the driver conversion series
and applied separately.
---
 drivers/net/phy/phylink.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 5d0af041b8f9..b45862465c4d 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -990,10 +990,10 @@ void phylink_start(struct phylink *pl)
 	}
 	if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state)
 		mod_timer(&pl->link_poll, jiffies + HZ);
-	if (pl->sfp_bus)
-		sfp_upstream_start(pl->sfp_bus);
 	if (pl->phydev)
 		phy_start(pl->phydev);
+	if (pl->sfp_bus)
+		sfp_upstream_start(pl->sfp_bus);
 }
 EXPORT_SYMBOL_GPL(phylink_start);
 
@@ -1010,10 +1010,10 @@ void phylink_stop(struct phylink *pl)
 {
 	ASSERT_RTNL();
 
-	if (pl->phydev)
-		phy_stop(pl->phydev);
 	if (pl->sfp_bus)
 		sfp_upstream_stop(pl->sfp_bus);
+	if (pl->phydev)
+		phy_stop(pl->phydev);
 	del_timer_sync(&pl->link_poll);
 	if (pl->link_irq) {
 		free_irq(pl->link_irq, pl);
-- 
2.22.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH v2] net: phylink: don't start and stop SGMII PHYs in SFP modules twice
  2019-07-24 13:31     ` [PATCH v2] " Arseny Solokha
@ 2019-07-24 13:36       ` Andrew Lunn
  2019-07-24 13:37       ` Russell King - ARM Linux admin
  2019-07-24 21:38       ` David Miller
  2 siblings, 0 replies; 26+ messages in thread
From: Andrew Lunn @ 2019-07-24 13:36 UTC (permalink / raw)
  To: Arseny Solokha; +Cc: Claudiu Manoil, Ioana Ciornei, Russell King, netdev

> This is a general fix and may be taken out from the driver conversion series
> and applied separately.

Hi Arseny

Yes please. Add a Fixes: tag and post it as a single patch for the net
tree.

Thanks
	Andrew

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v2] net: phylink: don't start and stop SGMII PHYs in SFP modules twice
  2019-07-24 13:31     ` [PATCH v2] " Arseny Solokha
  2019-07-24 13:36       ` Andrew Lunn
@ 2019-07-24 13:37       ` Russell King - ARM Linux admin
  2019-07-24 21:38       ` David Miller
  2 siblings, 0 replies; 26+ messages in thread
From: Russell King - ARM Linux admin @ 2019-07-24 13:37 UTC (permalink / raw)
  To: Arseny Solokha; +Cc: Claudiu Manoil, Ioana Ciornei, Andrew Lunn, netdev

On Wed, Jul 24, 2019 at 08:31:39PM +0700, Arseny Solokha wrote:
> SFP modules connected using the SGMII interface have their own PHYs which
> are handled by the struct phylink's phydev field. On the other hand, for
> the modules connected using 1000Base-X interface that field is not set.
> 
> Since commit ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network
> devices and sfp cages") phylink_start() ends up setting the phydev field
> using the sfp-bus infrastructure, which eventually calls phy_start() on it,
> and then calling phy_start() again on the same phydev from phylink_start()
> itself. Similar call sequence holds for phylink_stop(), only in the reverse
> order. This results in WARNs during network interface bringup and shutdown
> when a copper SFP module is connected, as phy_start() and phy_stop() are
> called twice in a row for the same phy_device:
> 
>   % ip link set up dev eth0
>   ------------[ cut here ]------------
>   called from state UP
>   WARNING: CPU: 1 PID: 155 at drivers/net/phy/phy.c:895 phy_start+0x74/0xc0
>   Modules linked in:
>   CPU: 1 PID: 155 Comm: backend Not tainted 5.2.0+ #1
>   NIP:  c0227bf0 LR: c0227bf0 CTR: c004d224
>   REGS: df547720 TRAP: 0700   Not tainted  (5.2.0+)
>   MSR:  00029000 <CE,EE,ME>  CR: 24002822  XER: 00000000
> 
>   GPR00: c0227bf0 df5477d8 df5d7080 00000014 df9d2370 df9d5ac4 1f4eb000 00000001
>   GPR08: c061fe58 00000000 00000000 df5477d8 0000003c 100c8768 00000000 00000000
>   GPR16: df486a00 c046f1c8 c046eea0 00000000 c046e904 c0239604 db68449c 00000000
>   GPR24: e9083204 00000000 00000001 db684460 e9083404 00000000 db6dce00 db6dcc00
>   NIP [c0227bf0] phy_start+0x74/0xc0
>   LR [c0227bf0] phy_start+0x74/0xc0
>   Call Trace:
>   [df5477d8] [c0227bf0] phy_start+0x74/0xc0 (unreliable)
>   [df5477e8] [c023cad0] startup_gfar+0x398/0x3f4
>   [df547828] [c023cf08] gfar_enet_open+0x364/0x374
>   [df547898] [c029d870] __dev_open+0xe4/0x140
>   [df5478c8] [c029db70] __dev_change_flags+0xf0/0x188
>   [df5478f8] [c029dc28] dev_change_flags+0x20/0x54
>   [df547918] [c02ae304] do_setlink+0x310/0x818
>   [df547a08] [c02b1eb8] __rtnl_newlink+0x384/0x6b0
>   [df547c28] [c02b222c] rtnl_newlink+0x48/0x68
>   [df547c48] [c02ad7c8] rtnetlink_rcv_msg+0x240/0x27c
>   [df547c98] [c02cc068] netlink_rcv_skb+0x8c/0xf0
>   [df547cd8] [c02cba3c] netlink_unicast+0x114/0x19c
>   [df547d08] [c02cbd74] netlink_sendmsg+0x2b0/0x2c0
>   [df547d58] [c027b668] sock_sendmsg_nosec+0x20/0x40
>   [df547d68] [c027d080] ___sys_sendmsg+0x17c/0x1dc
>   [df547e98] [c027df7c] __sys_sendmsg+0x68/0x84
>   [df547ef8] [c027e430] sys_socketcall+0x1a0/0x204
>   [df547f38] [c000d1d8] ret_from_syscall+0x0/0x38
>   --- interrupt: c01 at 0xfd4e030
>       LR = 0xfd4e010
>   Instruction dump:
>   813f0188 38800000 2b890005 419d0014 3d40c046 5529103a 394aa208 7c8a482e
>   3c60c046 3863a1b8 4cc63182 4be009a1 <0fe00000> 48000030 3c60c046 3863a1d0
>   ---[ end trace d4c095aeaf6ea998 ]---
> 
> and
> 
>   % ip link set down dev eth0
>   ------------[ cut here ]------------
>   called from state HALTED
>   WARNING: CPU: 1 PID: 184 at drivers/net/phy/phy.c:858 phy_stop+0x3c/0x88
> 
>   <...>
> 
>   Call Trace:
>   [df581788] [c0228450] phy_stop+0x3c/0x88 (unreliable)
>   [df581798] [c022d548] sfp_sm_phy_detach+0x1c/0x44
>   [df5817a8] [c022e8cc] sfp_sm_event+0x4b0/0x87c
>   [df581848] [c022f04c] sfp_upstream_stop+0x34/0x44
>   [df581858] [c0225608] phylink_stop+0x7c/0xe4
>   [df581868] [c023c57c] stop_gfar+0x7c/0x94
>   [df581888] [c023c5b8] gfar_close+0x24/0x94
>   [df5818a8] [c0298688] __dev_close_many+0xdc/0xf8
>   [df5818c8] [c029db58] __dev_change_flags+0xd8/0x188
>   [df5818f8] [c029dc28] dev_change_flags+0x20/0x54
>   [df581918] [c02ae304] do_setlink+0x310/0x818
>   [df581a08] [c02b1eb8] __rtnl_newlink+0x384/0x6b0
>   [df581c28] [c02b222c] rtnl_newlink+0x48/0x68
>   [df581c48] [c02ad7c8] rtnetlink_rcv_msg+0x240/0x27c
>   [df581c98] [c02cc068] netlink_rcv_skb+0x8c/0xf0
>   [df581cd8] [c02cba3c] netlink_unicast+0x114/0x19c
>   [df581d08] [c02cbd74] netlink_sendmsg+0x2b0/0x2c0
>   [df581d58] [c027b668] sock_sendmsg_nosec+0x20/0x40
>   [df581d68] [c027d080] ___sys_sendmsg+0x17c/0x1dc
>   [df581e98] [c027df7c] __sys_sendmsg+0x68/0x84
>   [df581ef8] [c027e430] sys_socketcall+0x1a0/0x204
>   [df581f38] [c000d1d8] ret_from_syscall+0x0/0x38
> 
>   <...>
> 
>   ---[ end trace d4c095aeaf6ea999 ]---
> 
> SFP modules with the 1000Base-X interface are not affected.
> 
> Place explicit calls to phy_start() and phy_stop() before enabling or after
> disabling an attached SFP module, where phydev is not yet set (or is
> already unset), so they will be made only from the inside of sfp-bus, if
> needed.
> 
> Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru>

Suggested-by: Russell King <rmk+kernel@armlinux.org.uk>
Acked-by: Russell King <rmk+kernel@armlinux.org.uk>

Dave, please merge this as a fix - it looks like it should be applied to
any kernel which also has:

217962615662 ("net: phy: warn if phy_start is called from invalid state")

i.o.w. v5.1 or later.

Thanks.

> ---
> Changes in v2:
>  - Moved phy_start() before sfp_upstream_start(), and phy_stop() after
>  sfp_upstream_stop(), and reworded the commit message accordingly.
> 
> This is a general fix and may be taken out from the driver conversion series
> and applied separately.
> ---
>  drivers/net/phy/phylink.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> index 5d0af041b8f9..b45862465c4d 100644
> --- a/drivers/net/phy/phylink.c
> +++ b/drivers/net/phy/phylink.c
> @@ -990,10 +990,10 @@ void phylink_start(struct phylink *pl)
>  	}
>  	if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state)
>  		mod_timer(&pl->link_poll, jiffies + HZ);
> -	if (pl->sfp_bus)
> -		sfp_upstream_start(pl->sfp_bus);
>  	if (pl->phydev)
>  		phy_start(pl->phydev);
> +	if (pl->sfp_bus)
> +		sfp_upstream_start(pl->sfp_bus);
>  }
>  EXPORT_SYMBOL_GPL(phylink_start);
>  
> @@ -1010,10 +1010,10 @@ void phylink_stop(struct phylink *pl)
>  {
>  	ASSERT_RTNL();
>  
> -	if (pl->phydev)
> -		phy_stop(pl->phydev);
>  	if (pl->sfp_bus)
>  		sfp_upstream_stop(pl->sfp_bus);
> +	if (pl->phydev)
> +		phy_stop(pl->phydev);
>  	del_timer_sync(&pl->link_poll);
>  	if (pl->link_irq) {
>  		free_irq(pl->link_irq, pl);
> -- 
> 2.22.0
> 
> 

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
According to speedtest.net: 11.9Mbps down 500kbps up

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH v2] net: phylink: don't start and stop SGMII PHYs in SFP modules twice
  2019-07-24 13:31     ` [PATCH v2] " Arseny Solokha
  2019-07-24 13:36       ` Andrew Lunn
  2019-07-24 13:37       ` Russell King - ARM Linux admin
@ 2019-07-24 21:38       ` David Miller
  2 siblings, 0 replies; 26+ messages in thread
From: David Miller @ 2019-07-24 21:38 UTC (permalink / raw)
  To: asolokha; +Cc: claudiu.manoil, ioana.ciornei, linux, andrew, netdev

From: Arseny Solokha <asolokha@kb.kras.ru>
Date: Wed, 24 Jul 2019 20:31:39 +0700

> SFP modules connected using the SGMII interface have their own PHYs which
> are handled by the struct phylink's phydev field. On the other hand, for
> the modules connected using 1000Base-X interface that field is not set.
> 
> Since commit ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network
> devices and sfp cages") phylink_start() ends up setting the phydev field
> using the sfp-bus infrastructure, which eventually calls phy_start() on it,
> and then calling phy_start() again on the same phydev from phylink_start()
> itself. Similar call sequence holds for phylink_stop(), only in the reverse
> order. This results in WARNs during network interface bringup and shutdown
> when a copper SFP module is connected, as phy_start() and phy_stop() are
> called twice in a row for the same phy_device:
 ...
> SFP modules with the 1000Base-X interface are not affected.
> 
> Place explicit calls to phy_start() and phy_stop() before enabling or after
> disabling an attached SFP module, where phydev is not yet set (or is
> already unset), so they will be made only from the inside of sfp-bus, if
> needed.
> 
> Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru>

Applied with appropriate Fixes: tag added and queued up for -stable.

Thanks.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/2] gianfar: convert to phylink
  2019-07-23 15:17 ` [RFC PATCH 1/2] gianfar: convert " Arseny Solokha
  2019-07-23 16:07   ` Claudiu Manoil
  2019-07-24  8:19   ` Russell King - ARM Linux admin
@ 2019-07-29 23:39   ` Vladimir Oltean
  2019-07-30 10:23     ` Russell King - ARM Linux admin
                       ` (2 more replies)
  2 siblings, 3 replies; 26+ messages in thread
From: Vladimir Oltean @ 2019-07-29 23:39 UTC (permalink / raw)
  To: Arseny Solokha, Claudiu Manoil, Russell King
  Cc: Ioana Ciornei, Andrew Lunn, netdev, Florian Fainelli

Hi Arseny,

Nice project!

On Wed, 24 Jul 2019 at 03:38, Arseny Solokha <asolokha@kb.kras.ru> wrote:
>
> Convert gianfar to use the phylink API for better SFP modules support.
>
> The driver still uses phylib for serdes configuration over the TBI
> interface, as there seems to be no functionally equivalent API present
> in phylink (yet). phylib usage is basically confined in two functions.
>
> One needs to change their Device Tree accordingly to get working SFP
> support:
>
>  enet1: ethernet@25000 {
>  <...>
>         device_type = "network";
>         model = "eTSEC";
>         compatible = "gianfar";
>         tbi-handle = <&tbi0>;
> +       phy-connection-type = "sgmii";
> +       managed = "in-band-status";
> +       sfp = <&sfp>;
>         max-speed = <1000>;
>
>         mdio@520 {
>                 #address-cells = <1>;
>                 #size-cells = <0>;
>                 compatible = "fsl,gianfar-tbi";
>                 reg = <0x520 0x20>;
>
>                 tbi0: tbi-phy@1f {
>                         reg = <0x1f>;
>                         device_type = "tbi-phy";
>                 };
>         };
> +
> +       sfp: sfp0 {
> +               compatible = "sff,sfp";
> +               i2c-bus = <&i2c1>;
> +               mod-def0-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
> +               rate-select0-gpios = <&gpio 13 GPIO_ACTIVE_HIGH>;
> +               tx-disable-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
> +               tx-fault-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>;
> +               los-gpios = <&gpio 3 GPIO_ACTIVE_HIGH>;
> +       };
>  };
>
> Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru>
> ---
>  drivers/net/ethernet/freescale/Kconfig        |   2 +-
>  drivers/net/ethernet/freescale/gianfar.c      | 409 +++++++++---------
>  drivers/net/ethernet/freescale/gianfar.h      |  26 +-
>  .../net/ethernet/freescale/gianfar_ethtool.c  |  79 ++--
>  4 files changed, 251 insertions(+), 265 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig
> index 6a7e8993119f..8b51d423b61d 100644
> --- a/drivers/net/ethernet/freescale/Kconfig
> +++ b/drivers/net/ethernet/freescale/Kconfig
> @@ -89,7 +89,7 @@ config GIANFAR
>         tristate "Gianfar Ethernet"
>         depends on HAS_DMA
>         select FSL_PQ_MDIO
> -       select PHYLIB
> +       select PHYLINK
>         select CRC32
>         ---help---
>           This driver supports the Gigabit TSEC on the MPC83xx, MPC85xx,
> diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
> index 7ea19e173339..64c7b174e591 100644
> --- a/drivers/net/ethernet/freescale/gianfar.c
> +++ b/drivers/net/ethernet/freescale/gianfar.c
> @@ -96,6 +96,7 @@
>  #include <linux/mii.h>
>  #include <linux/phy.h>
>  #include <linux/phy_fixed.h>
> +#include <linux/phylink.h>

You can remove linux/phy.h.
You (PHYLINK, actually) broke .ndo_change_carrier, and therefore
commit 6211d46713c5 ("gianfar: Add change_carrier() for Fixed PHYs").
If we were honest, we should probably revert that commit entirely now.
Once you do that, you can remove linux/phy_fixed.h as well.

>  #include <linux/of.h>
>  #include <linux/of_net.h>
>
> @@ -117,8 +118,18 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu);
>  static irqreturn_t gfar_error(int irq, void *dev_id);
>  static irqreturn_t gfar_transmit(int irq, void *dev_id);
>  static irqreturn_t gfar_interrupt(int irq, void *dev_id);
> -static void adjust_link(struct net_device *dev);
> -static noinline void gfar_update_link_state(struct gfar_private *priv);
> +static void gfar_phylink_validate(struct phylink_config *config,
> +                                 unsigned long *supported,
> +                                 struct phylink_link_state *state);
> +static int gfar_mac_link_state(struct phylink_config *config,
> +                              struct phylink_link_state *state);
> +static void gfar_mac_config(struct phylink_config *config, unsigned int mode,
> +                           const struct phylink_link_state *state);
> +static void gfar_mac_an_restart(struct phylink_config *config);
> +static void gfar_mac_link_down(struct phylink_config *config, unsigned int mode,
> +                              phy_interface_t interface);
> +static void gfar_mac_link_up(struct phylink_config *config, unsigned int mode,
> +                            phy_interface_t interface, struct phy_device *phy);

Please do not add to this forward declaration madness. A cleanup patch
in this area would be highly appreciated.

>  static int init_phy(struct net_device *dev);
>  static int gfar_probe(struct platform_device *ofdev);
>  static int gfar_remove(struct platform_device *ofdev);
> @@ -504,6 +515,15 @@ static const struct net_device_ops gfar_netdev_ops = {
>  #endif
>  };
>
> +static const struct phylink_mac_ops gfar_phylink_ops = {
> +       .validate = gfar_phylink_validate,
> +       .mac_link_state = gfar_mac_link_state,
> +       .mac_config = gfar_mac_config,
> +       .mac_an_restart = gfar_mac_an_restart,
> +       .mac_link_down = gfar_mac_link_down,
> +       .mac_link_up = gfar_mac_link_up,
> +};
> +
>  static void gfar_ints_disable(struct gfar_private *priv)
>  {
>         int i;
> @@ -733,6 +753,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
>         struct gfar_private *priv = NULL;
>         struct device_node *np = ofdev->dev.of_node;
>         struct device_node *child = NULL;
> +       struct phylink *phylink;
>         u32 stash_len = 0;
>         u32 stash_idx = 0;
>         unsigned int num_tx_qs, num_rx_qs;
> @@ -891,11 +912,21 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
>
>         err = of_property_read_string(np, "phy-connection-type", &ctype);
>
> -       /* We only care about rgmii-id.  The rest are autodetected */
> -       if (err == 0 && !strcmp(ctype, "rgmii-id"))
> -               priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
> -       else
> +       /* We only care about rgmii-id and sgmii - the former
> +        * is indistinguishable from rgmii in hardware, and phylink needs
> +        * the latter to be set appropriately for correct phy configuration.
> +        * The rest are autodetected
> +        */
> +       if (err == 0) {
> +               if (!strcmp(ctype, "rgmii-id"))
> +                       priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
> +               else if (!strcmp(ctype, "sgmii"))
> +                       priv->interface = PHY_INTERFACE_MODE_SGMII;
> +               else
> +                       priv->interface = PHY_INTERFACE_MODE_MII;
> +       } else {
>                 priv->interface = PHY_INTERFACE_MODE_MII;
> +       }
>

No. Don't do this. Just do:

    err = of_get_phy_mode(np);
    if (err < 0)
        goto err_grp_init;

    priv->interface = err;

>         if (of_find_property(np, "fsl,magic-packet", NULL))
>                 priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
> @@ -903,19 +934,21 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
>         if (of_get_property(np, "fsl,wake-on-filer", NULL))
>                 priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER;
>
> -       priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
> +       priv->device_node = np;
> +       priv->speed = SPEED_UNKNOWN;
>
> -       /* In the case of a fixed PHY, the DT node associated
> -        * to the PHY is the Ethernet MAC DT node.
> -        */
> -       if (!priv->phy_node && of_phy_is_fixed_link(np)) {
> -               err = of_phy_register_fixed_link(np);
> -               if (err)
> -                       goto err_grp_init;
> +       priv->phylink_config.dev = &priv->ndev->dev;
> +       priv->phylink_config.type = PHYLINK_NETDEV;
>
> -               priv->phy_node = of_node_get(np);
> +       phylink = phylink_create(&priv->phylink_config, of_fwnode_handle(np),
> +                                priv->interface, &gfar_phylink_ops);

You introduced a bug here.
of_phy_connect used to take the PHY interface type (for good or bad)
from gfar_get_interface() (which is reconstructing it from the MAC
registers).
You are now passing the PHY interface type to phylink_create from the
"phy-connection-type" DT property.
At the very least, you are breaking LS1021A which uses phy-mode
instead of phy-connection-type (hence my comment above to use the
generic OF helper).
Actually I think you just uncovered a latent bug, in that the DT
bindings for phy-mode didn't mean much at all to the driver - it would
rely on what the bootloader had set up.
Actually DT bindings for phy-connection-type were most likely simply
bolt on on top of gianfar when they figured they couldn't just
auto-detect the various species of required RGMII delays.
But gfar_get_interface is a piece of history that was introduced in
the same commit as the enum phy_interface_t itself: e8a2b6a42073
("[PATCH] PHY: Add support for configuring the PHY connection
interface"). Its time has come.

> +       if (IS_ERR(phylink)) {
> +               err = PTR_ERR(phylink);
> +               goto err_grp_init;
>         }
>
> +       priv->phylink = phylink;
> +
>         /* Find the TBI PHY.  If it's not there, we don't support SGMII */
>         priv->tbi_node = of_parse_phandle(np, "tbi-handle", 0);
>
> @@ -994,7 +1027,7 @@ static int gfar_hwtstamp_get(struct net_device *netdev, struct ifreq *ifr)
>
>  static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
>  {
> -       struct phy_device *phydev = dev->phydev;
> +       struct gfar_private *priv = netdev_priv(dev);
>
>         if (!netif_running(dev))
>                 return -EINVAL;
> @@ -1004,10 +1037,7 @@ static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
>         if (cmd == SIOCGHWTSTAMP)
>                 return gfar_hwtstamp_get(dev, rq);
>
> -       if (!phydev)
> -               return -ENODEV;
> -
> -       return phy_mii_ioctl(phydev, rq, cmd);
> +       return phylink_mii_ioctl(priv->phylink, rq, cmd);
>  }
>
>  static u32 cluster_entry_per_class(struct gfar_private *priv, u32 rqfar,
> @@ -1307,7 +1337,6 @@ static void gfar_init_addr_hash_table(struct gfar_private *priv)
>   */
>  static int gfar_probe(struct platform_device *ofdev)
>  {
> -       struct device_node *np = ofdev->dev.of_node;
>         struct net_device *dev = NULL;
>         struct gfar_private *priv = NULL;
>         int err = 0, i;
> @@ -1463,12 +1492,10 @@ static int gfar_probe(struct platform_device *ofdev)
>         return 0;
>
>  register_fail:
> -       if (of_phy_is_fixed_link(np))
> -               of_phy_deregister_fixed_link(np);
>         unmap_group_regs(priv);
>         gfar_free_rx_queues(priv);
>         gfar_free_tx_queues(priv);
> -       of_node_put(priv->phy_node);
> +       phylink_destroy(priv->phylink);
>         of_node_put(priv->tbi_node);
>         free_gfar_dev(priv);
>         return err;
> @@ -1477,19 +1504,15 @@ static int gfar_probe(struct platform_device *ofdev)
>  static int gfar_remove(struct platform_device *ofdev)
>  {
>         struct gfar_private *priv = platform_get_drvdata(ofdev);
> -       struct device_node *np = ofdev->dev.of_node;
>
> -       of_node_put(priv->phy_node);
>         of_node_put(priv->tbi_node);
>
>         unregister_netdev(priv->ndev);
>
> -       if (of_phy_is_fixed_link(np))
> -               of_phy_deregister_fixed_link(np);
> -
>         unmap_group_regs(priv);
>         gfar_free_rx_queues(priv);
>         gfar_free_tx_queues(priv);
> +       phylink_destroy(priv->phylink);
>         free_gfar_dev(priv);
>
>         return 0;
> @@ -1643,9 +1666,11 @@ static int gfar_suspend(struct device *dev)
>                 gfar_start_wol_filer(priv);
>
>         } else {
> -               phy_stop(ndev->phydev);
> +               phylink_stop(phy->phylink);
>         }
>
> +       priv->speed = SPEED_UNKNOWN;
> +
>         return 0;
>  }
>
> @@ -1672,7 +1697,7 @@ static int gfar_resume(struct device *dev)
>                 gfar_filer_restore_table(priv);
>
>         } else {
> -               phy_start(ndev->phydev);
> +               phylink_start(priv->phylink);
>         }
>
>         gfar_start(priv);
> @@ -1702,12 +1727,7 @@ static int gfar_restore(struct device *dev)
>
>         gfar_start(priv);
>
> -       priv->oldlink = 0;
> -       priv->oldspeed = 0;
> -       priv->oldduplex = -1;
> -
> -       if (ndev->phydev)
> -               phy_start(ndev->phydev);
> +       phylink_start(priv->phylink);
>
>         netif_device_attach(ndev);
>         enable_napi(priv);
> @@ -1781,46 +1801,26 @@ static phy_interface_t gfar_get_interface(struct net_device *dev)
>   */
>  static int init_phy(struct net_device *dev)
>  {
> -       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
>         struct gfar_private *priv = netdev_priv(dev);
>         phy_interface_t interface;
> -       struct phy_device *phydev;
>         struct ethtool_eee edata;
> +       int flags = 0;
> +       int err;
>
> -       linkmode_set_bit_array(phy_10_100_features_array,
> -                              ARRAY_SIZE(phy_10_100_features_array),
> -                              mask);
> -       linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask);
> -       linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, mask);
> -       if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
> -               linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mask);
> -
> -       priv->oldlink = 0;
> -       priv->oldspeed = 0;
> -       priv->oldduplex = -1;
> -
> -       interface = gfar_get_interface(dev);
> -
> -       phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0,
> -                               interface);
> -       if (!phydev) {
> -               dev_err(&dev->dev, "could not attach to PHY\n");
> -               return -ENODEV;
> +       err = phylink_of_phy_connect(priv->phylink, priv->device_node, flags);
> +       if (err) {
> +               netdev_err(dev, "could not attach to PHY: %d\n", err);
> +               return err;

While you're at it, can you say "connect" instead of "attach" here?

>         }
>
> +       priv->tbi_phy = NULL;
> +       interface = gfar_get_interface(dev);

Be consistent and just go for priv->interface. Nobody's changing it anyway.

>         if (interface == PHY_INTERFACE_MODE_SGMII)
>                 gfar_configure_serdes(dev);
>

My gut feeling says that this should be moved to gfar_mac_config.

> -       /* Remove any features not supported by the controller */
> -       linkmode_and(phydev->supported, phydev->supported, mask);
> -       linkmode_copy(phydev->advertising, phydev->supported);
> -
> -       /* Add support for flow control */
> -       phy_support_asym_pause(phydev);
> -
>         /* disable EEE autoneg, EEE not supported by eTSEC */
>         memset(&edata, 0, sizeof(struct ethtool_eee));
> -       phy_ethtool_set_eee(phydev, &edata);
> +       phylink_ethtool_set_eee(priv->phylink, &edata);
>
>         return 0;
>  }
> @@ -1850,6 +1850,8 @@ static void gfar_configure_serdes(struct net_device *dev)
>                 return;
>         }
>
> +       priv->tbi_phy = tbiphy;
> +
>         /* If the link is already up, we must already be ok, and don't need to
>          * configure and reset the TBI<->SerDes link.  Maybe U-Boot configured
>          * everything for us?  Resetting it takes the link down and requires
> @@ -1964,7 +1966,7 @@ void stop_gfar(struct net_device *dev)
>         /* disable ints and gracefully shut down Rx/Tx DMA */
>         gfar_halt(priv);
>
> -       phy_stop(dev->phydev);
> +       phylink_stop(priv->phylink);
>
>         free_skb_resources(priv);
>  }
> @@ -2219,12 +2221,7 @@ int startup_gfar(struct net_device *ndev)
>         /* Start Rx/Tx DMA and enable the interrupts */
>         gfar_start(priv);
>
> -       /* force link state update after mac reset */
> -       priv->oldlink = 0;
> -       priv->oldspeed = 0;
> -       priv->oldduplex = -1;
> -
> -       phy_start(ndev->phydev);
> +       phylink_start(priv->phylink);
>
>         enable_napi(priv);
>
> @@ -2593,7 +2590,7 @@ static int gfar_close(struct net_device *dev)
>         stop_gfar(dev);
>
>         /* Disconnect from the PHY */
> -       phy_disconnect(dev->phydev);
> +       phylink_disconnect_phy(priv->phylink);

It is very odd to disconnect from the PHY on ndo_close and connect
back on ndo_open. I don't know of any other driver that does that.
Can't you change the behavior to simply start and stop phylink here?

>
>         gfar_free_irq(priv);
>
> @@ -3387,23 +3384,6 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id)
>         return IRQ_HANDLED;
>  }
>
> -/* Called every time the controller might need to be made
> - * aware of new link state.  The PHY code conveys this
> - * information through variables in the phydev structure, and this
> - * function converts those variables into the appropriate
> - * register values, and can bring down the device if needed.
> - */
> -static void adjust_link(struct net_device *dev)
> -{
> -       struct gfar_private *priv = netdev_priv(dev);
> -       struct phy_device *phydev = dev->phydev;
> -
> -       if (unlikely(phydev->link != priv->oldlink ||
> -                    (phydev->link && (phydev->duplex != priv->oldduplex ||
> -                                      phydev->speed != priv->oldspeed))))
> -               gfar_update_link_state(priv);
> -}

Getting rid of the cruft from this function deserves its own patch.

> -
>  /* Update the hash table based on the current list of multicast
>   * addresses we subscribe to.  Also, change the promiscuity of
>   * the device based on the flags (this function is called
> @@ -3635,132 +3615,169 @@ static irqreturn_t gfar_error(int irq, void *grp_id)
>         return IRQ_HANDLED;
>  }
>
> -static u32 gfar_get_flowctrl_cfg(struct gfar_private *priv)
> +static void gfar_phylink_validate(struct phylink_config *config,
> +                                 unsigned long *supported,
> +                                 struct phylink_link_state *state)
> +{
> +       struct gfar_private *priv = netdev_priv(to_net_dev(config->dev));
> +       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
> +
> +       if (state->interface != PHY_INTERFACE_MODE_NA &&
> +           state->interface != PHY_INTERFACE_MODE_SGMII &&
> +           state->interface != PHY_INTERFACE_MODE_1000BASEX &&

Russell is right, you shouldn't advertise 1000Base-X if there is no
way of making sure that it works.
His question was:

> How is the difference between SGMII and 1000BASE-X handled?

To be honest I don't have a complete answer to that question. The
literature recommends writing 0x01a0 to the MII_ADVERTISE (0x4)
register of the MAC PCS for 1000Base-X, and 0x4001 for SGMII.
The FMan driver which uses the TSEC MAC does exactly that:
https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git/tree/drivers/net/ethernet/freescale/fman/fman_dtsec.c#n58
However I can't seem to be able to trace down the definition of bit 14
from 0x4001 - it's reserved in all the manuals I see. I have a hunch
that it selects the format of the Config_Reg base page between
1000Base-X and SGMII.
But the bad news is that gianfar is only configuring the TBI PHY for
the 1000Base-X Config_Reg, even for SGMII PHYs. I'm not quite sure,
but I think that the only reason that this doesn't break in-band AN is
that we don't use in-band AN.

> +           !phy_interface_mode_is_rgmii(state->interface)) {
> +               phylink_zero(supported);
> +               return;
> +       }
> +
> +       phylink_set(mask, Autoneg);
> +       phylink_set(mask, Pause);
> +       phylink_set(mask, Asym_Pause);
> +       phylink_set_port_modes(mask);
> +
> +       phylink_set(mask, 10baseT_Half);
> +       phylink_set(mask, 10baseT_Full);
> +       phylink_set(mask, 100baseT_Half);
> +       phylink_set(mask, 100baseT_Full);
> +
> +       if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT) {
> +               phylink_set(mask, 1000baseX_Full);
> +               phylink_set(mask, 1000baseT_Full);
> +       }
> +
> +       linkmode_and(supported, supported, mask);
> +       linkmode_and(state->advertising, state->advertising, mask);
> +}
> +
> +static int gfar_mac_link_state(struct phylink_config *config,
> +                              struct phylink_link_state *state)
> +{
> +       if (state->interface == PHY_INTERFACE_MODE_SGMII ||
> +           state->interface == PHY_INTERFACE_MODE_1000BASEX) {

What if you reduce the indentation level by 1 here, by just exiting if
the interface mode is not SGMII?

> +               struct gfar_private *priv =
> +                       netdev_priv(to_net_dev(config->dev));
> +               u16 tbi_cr;
> +
> +               if (!priv->tbi_phy)
> +                       return -ENODEV;
> +
> +               tbi_cr = phy_read(priv->tbi_phy, MII_TBI_CR);
> +
> +               state->duplex = !!(tbi_cr & TBI_CR_FULL_DUPLEX);

Woah there. Aren't you supposed to first ensure state->an_complete is
ok, based on TBI_MII_Register_Set_SR[AN_Done]? There's also a
Link_Status bit in that register that you could retrieve.

> +               if ((tbi_cr & TBI_CR_SPEED_1000_MASK) == TBI_CR_SPEED_1000_MASK)
> +                       state->speed = SPEED_1000;
> +       }

See the Speed_Bit table from TBI_MII_Register_Set_ANLPBPA_SGMII for
the link partner (aka SGMII PHY) advertisement. You have to do a
logical-and between that and your own. Also please make sure you
really are in SGMII AN and not 1000 Base-X when interpreting registers
4 & 5 as one way or another.

> +
> +       return 1;
> +}
> +
> +static u32 gfar_get_flowctrl_cfg(const struct phylink_link_state *state)
>  {
> -       struct net_device *ndev = priv->ndev;
> -       struct phy_device *phydev = ndev->phydev;
>         u32 val = 0;
>
> -       if (!phydev->duplex)
> +       if (!state->duplex)
>                 return val;
>
> -       if (!priv->pause_aneg_en) {
> -               if (priv->tx_pause_en)
> -                       val |= MACCFG1_TX_FLOW;
> -               if (priv->rx_pause_en)
> -                       val |= MACCFG1_RX_FLOW;
> -       } else {
> -               u16 lcl_adv, rmt_adv;
> -               u8 flowctrl;
> -               /* get link partner capabilities */
> -               rmt_adv = 0;
> -               if (phydev->pause)
> -                       rmt_adv = LPA_PAUSE_CAP;
> -               if (phydev->asym_pause)
> -                       rmt_adv |= LPA_PAUSE_ASYM;
> -
> -               lcl_adv = linkmode_adv_to_lcl_adv_t(phydev->advertising);
> -               flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
> -               if (flowctrl & FLOW_CTRL_TX)
> -                       val |= MACCFG1_TX_FLOW;
> -               if (flowctrl & FLOW_CTRL_RX)
> -                       val |= MACCFG1_RX_FLOW;
> -       }
> +       if (state->pause & MLO_PAUSE_TX)
> +               val |= MACCFG1_TX_FLOW;
> +       if (state->pause & MLO_PAUSE_RX)
> +               val |= MACCFG1_RX_FLOW;
>
>         return val;
>  }
>
> -static noinline void gfar_update_link_state(struct gfar_private *priv)
> +static void gfar_mac_config(struct phylink_config *config, unsigned int mode,
> +                           const struct phylink_link_state *state)
>  {
> +       struct gfar_private *priv = netdev_priv(to_net_dev(config->dev));
>         struct gfar __iomem *regs = priv->gfargrp[0].regs;
> -       struct net_device *ndev = priv->ndev;
> -       struct phy_device *phydev = ndev->phydev;
> -       struct gfar_priv_rx_q *rx_queue = NULL;
> -       int i;
> +       u32 maccfg1, new_maccfg1;
> +       u32 maccfg2, new_maccfg2;
> +       u32 ecntrl, new_ecntrl;
> +       u32 tx_flow, new_tx_flow;

Don't introduce new_ variables. Is there any issue if you
unconditionally write to the MAC registers?

>
>         if (unlikely(test_bit(GFAR_RESETTING, &priv->state)))
>                 return;
>
> -       if (phydev->link) {
> -               u32 tempval1 = gfar_read(&regs->maccfg1);
> -               u32 tempval = gfar_read(&regs->maccfg2);
> -               u32 ecntrl = gfar_read(&regs->ecntrl);
> -               u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW);
> +       if (unlikely(phylink_autoneg_inband(mode)))
> +               return;
>
> -               if (phydev->duplex != priv->oldduplex) {
> -                       if (!(phydev->duplex))
> -                               tempval &= ~(MACCFG2_FULL_DUPLEX);
> -                       else
> -                               tempval |= MACCFG2_FULL_DUPLEX;
> +       maccfg1 = gfar_read(&regs->maccfg1);
> +       maccfg2 = gfar_read(&regs->maccfg2);
> +       ecntrl = gfar_read(&regs->ecntrl);
>
> -                       priv->oldduplex = phydev->duplex;
> -               }
> +       new_maccfg2 = maccfg2 & ~(MACCFG2_FULL_DUPLEX | MACCFG2_IF);
> +       new_ecntrl = ecntrl & ~ECNTRL_R100;
>
> -               if (phydev->speed != priv->oldspeed) {
> -                       switch (phydev->speed) {
> -                       case 1000:
> -                               tempval =
> -                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
> +       if (state->duplex)
> +               new_maccfg2 |= MACCFG2_FULL_DUPLEX;
>
> -                               ecntrl &= ~(ECNTRL_R100);
> -                               break;
> -                       case 100:
> -                       case 10:
> -                               tempval =
> -                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
> -
> -                               /* Reduced mode distinguishes
> -                                * between 10 and 100
> -                                */
> -                               if (phydev->speed == SPEED_100)
> -                                       ecntrl |= ECNTRL_R100;
> -                               else
> -                                       ecntrl &= ~(ECNTRL_R100);
> -                               break;
> -                       default:
> -                               netif_warn(priv, link, priv->ndev,
> -                                          "Ack!  Speed (%d) is not 10/100/1000!\n",
> -                                          phydev->speed);

Please 1. remove "Ack!" 2. treat SPEED_UNKNOWN here by setting the MAC
into a low-power state (e.g. 10 Mbps - the power savings are real).
Don't print that Speed -1 is not 10/100/1000, we know that.

> -                               break;
> -                       }
> -
> -                       priv->oldspeed = phydev->speed;
> -               }
> -
> -               tempval1 &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
> -               tempval1 |= gfar_get_flowctrl_cfg(priv);
> -
> -               /* Turn last free buffer recording on */
> -               if ((tempval1 & MACCFG1_TX_FLOW) && !tx_flow_oldval) {
> -                       for (i = 0; i < priv->num_rx_queues; i++) {
> -                               u32 bdp_dma;
> -
> -                               rx_queue = priv->rx_queue[i];
> -                               bdp_dma = gfar_rxbd_dma_lastfree(rx_queue);
> -                               gfar_write(rx_queue->rfbptr, bdp_dma);
> -                       }
> -
> -                       priv->tx_actual_en = 1;
> -               }
> -
> -               if (unlikely(!(tempval1 & MACCFG1_TX_FLOW) && tx_flow_oldval))
> -                       priv->tx_actual_en = 0;
> -
> -               gfar_write(&regs->maccfg1, tempval1);
> -               gfar_write(&regs->maccfg2, tempval);
> -               gfar_write(&regs->ecntrl, ecntrl);
> -
> -               if (!priv->oldlink)
> -                       priv->oldlink = 1;
> -
> -       } else if (priv->oldlink) {
> -               priv->oldlink = 0;
> -               priv->oldspeed = 0;
> -               priv->oldduplex = -1;
> +       switch (state->speed) {
> +       case SPEED_1000:
> +               new_maccfg2 |= MACCFG2_GMII;
> +               break;
> +       case SPEED_100:
> +               new_maccfg2 |= MACCFG2_MII;
> +               new_ecntrl = ecntrl | ECNTRL_R100;
> +               break;
> +       case SPEED_10:
> +               new_maccfg2 |= MACCFG2_MII;
> +               break;
> +       default:
> +               netif_warn(priv, link, priv->ndev,
> +                          "Ack!  Speed (%d) is not 10/100/1000!\n",
> +                          state->speed);
> +               return;
>         }
>
> -       if (netif_msg_link(priv))
> -               phy_print_status(phydev);
> +       priv->speed = state->speed;
> +
> +       new_maccfg1 = maccfg1 & ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
> +       new_maccfg1 |= gfar_get_flowctrl_cfg(state);
> +
> +       /* Turn last free buffer recording on */
> +       tx_flow = maccfg1 & MACCFG1_TX_FLOW;
> +       new_tx_flow = new_maccfg1 & MACCFG1_TX_FLOW;
> +       if (new_tx_flow && !tx_flow) {
> +               int i;
> +
> +               for (i = 0; i < priv->num_rx_queues; i++) {
> +                       struct gfar_priv_rx_q *rx_queue;
> +                       u32 bdp_dma;
> +
> +                       rx_queue = priv->rx_queue[i];
> +                       bdp_dma = gfar_rxbd_dma_lastfree(rx_queue);
> +                       gfar_write(rx_queue->rfbptr, bdp_dma);
> +               }
> +
> +               priv->tx_actual_en = 1;
> +       } else if (unlikely(!new_tx_flow && tx_flow)) {
> +               priv->tx_actual_en = 0;
> +       }
> +
> +       if (new_maccfg1 != maccfg1)
> +               gfar_write(&regs->maccfg1, new_maccfg1);
> +       if (new_maccfg2 != maccfg2)
> +               gfar_write(&regs->maccfg2, new_maccfg2);
> +       if (new_ecntrl != ecntrl)
> +               gfar_write(&regs->ecntrl, new_ecntrl);
> +}
> +
> +static void gfar_mac_an_restart(struct phylink_config *config)
> +{
> +       /* Not supported */
> +}

What about running gfar_configure_serdes again?

> +
> +static void gfar_mac_link_down(struct phylink_config *config, unsigned int mode,
> +                              phy_interface_t interface)
> +{
> +       /* Not supported */
> +}
> +

What about disabling RX_EN and TX_EN from MACCFG1?

> +static void gfar_mac_link_up(struct phylink_config *config, unsigned int mode,
> +                            phy_interface_t interface, struct phy_device *phy)
> +{
> +       /* Not supported */
>  }
>

What about enabling RX_EN and TX_EN from MACCFG1?

>  static const struct of_device_id gfar_match[] =
> diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h
> index f2af96349c7b..0b28b1f60f2d 100644
> --- a/drivers/net/ethernet/freescale/gianfar.h
> +++ b/drivers/net/ethernet/freescale/gianfar.h
> @@ -31,8 +31,7 @@
>  #include <linux/skbuff.h>
>  #include <linux/spinlock.h>
>  #include <linux/mm.h>
> -#include <linux/mii.h>
> -#include <linux/phy.h>
> +#include <linux/phylink.h>
>
>  #include <asm/io.h>
>  #include <asm/irq.h>
> @@ -149,8 +148,13 @@ extern const char gfar_driver_version[];
>  #define GFAR_SUPPORTED_GBIT SUPPORTED_1000baseT_Full
>
>  /* TBI register addresses */
> +#define MII_TBI_CR             0x00
>  #define MII_TBICON             0x11
>
> +/* TBI_CR register bit fields */
> +#define TBI_CR_FULL_DUPLEX     0x0100
> +#define TBI_CR_SPEED_1000_MASK 0x0040
> +

I think BIT() definitions are preferred, even if that means you have
to convert existing code first.

>  /* TBICON register bit fields */
>  #define TBICON_CLK_SELECT      0x0020
>
> @@ -1148,12 +1152,12 @@ struct gfar_private {
>
>         /* PHY stuff */
>         phy_interface_t interface;
> -       struct device_node *phy_node;
> +       struct device_node *device_node;
>         struct device_node *tbi_node;
> -       struct mii_bus *mii_bus;
> -       int oldspeed;
> -       int oldduplex;
> -       int oldlink;
> +       struct phylink *phylink;
> +       struct phylink_config phylink_config;
> +       struct phy_device *tbi_phy;
> +       int speed;
>
>         uint32_t msg_enable;
>
> @@ -1165,11 +1169,7 @@ struct gfar_private {
>                 bd_stash_en:1,
>                 rx_filer_enable:1,
>                 /* Enable priorty based Tx scheduling in Hw */
> -               prio_sched_en:1,
> -               /* Flow control flags */
> -               pause_aneg_en:1,
> -               tx_pause_en:1,
> -               rx_pause_en:1;
> +               prio_sched_en:1;
>
>         /* The total tx and rx ring size for the enabled queues */
>         unsigned int total_tx_ring_size;
> @@ -1333,8 +1333,6 @@ void reset_gfar(struct net_device *dev);
>  void gfar_mac_reset(struct gfar_private *priv);
>  void gfar_halt(struct gfar_private *priv);
>  void gfar_start(struct gfar_private *priv);
> -void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable,
> -                  u32 regnum, u32 read);
>  void gfar_configure_coalescing_all(struct gfar_private *priv);
>  int gfar_set_features(struct net_device *dev, netdev_features_t features);
>
> diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
> index 3433b46b90c1..146b30d07789 100644
> --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
> +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
> @@ -35,7 +35,7 @@
>  #include <asm/types.h>
>  #include <linux/ethtool.h>
>  #include <linux/mii.h>
> -#include <linux/phy.h>
> +#include <linux/phylink.h>
>  #include <linux/sort.h>
>  #include <linux/if_vlan.h>
>  #include <linux/of_platform.h>
> @@ -207,12 +207,10 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs,
>  static unsigned int gfar_usecs2ticks(struct gfar_private *priv,
>                                      unsigned int usecs)
>  {
> -       struct net_device *ndev = priv->ndev;
> -       struct phy_device *phydev = ndev->phydev;

Are you sure this still works? You missed a ndev->phydev check from
gfar_gcoalesce, where this is called from. Technically you can still
check ndev->phydev, it's just that PHYLINK doesn't guarantee you'll
have one (e.g. fixed-link interfaces).

>         unsigned int count;
>
>         /* The timer is different, depending on the interface speed */
> -       switch (phydev->speed) {
> +       switch (priv->speed) {

What about retrieving the speed from phylink_ethtool_ksettings_get,
and using that here? This would save you from introducing a cached
priv->speed.

>         case SPEED_1000:
>                 count = GFAR_GBIT_TIME;
>                 break;
> @@ -234,12 +232,10 @@ static unsigned int gfar_usecs2ticks(struct gfar_private *priv,
>  static unsigned int gfar_ticks2usecs(struct gfar_private *priv,
>                                      unsigned int ticks)
>  {
> -       struct net_device *ndev = priv->ndev;
> -       struct phy_device *phydev = ndev->phydev;
>         unsigned int count;
>
>         /* The timer is different, depending on the interface speed */
> -       switch (phydev->speed) {
> +       switch (priv->speed) {

Same comments as above, but for gfar_scoalesce.

>         case SPEED_1000:
>                 count = GFAR_GBIT_TIME;
>                 break;
> @@ -489,58 +485,15 @@ static void gfar_gpauseparam(struct net_device *dev,
>  {
>         struct gfar_private *priv = netdev_priv(dev);
>
> -       epause->autoneg = !!priv->pause_aneg_en;
> -       epause->rx_pause = !!priv->rx_pause_en;
> -       epause->tx_pause = !!priv->tx_pause_en;
> +       phylink_ethtool_get_pauseparam(priv->phylink, epause);
>  }
>
>  static int gfar_spauseparam(struct net_device *dev,
>                             struct ethtool_pauseparam *epause)
>  {
>         struct gfar_private *priv = netdev_priv(dev);
> -       struct phy_device *phydev = dev->phydev;
> -       struct gfar __iomem *regs = priv->gfargrp[0].regs;
>
> -       if (!phydev)
> -               return -ENODEV;
> -
> -       if (!phy_validate_pause(phydev, epause))
> -               return -EINVAL;
> -
> -       priv->rx_pause_en = priv->tx_pause_en = 0;
> -       phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause);
> -       if (epause->rx_pause) {
> -               priv->rx_pause_en = 1;
> -
> -               if (epause->tx_pause) {
> -                       priv->tx_pause_en = 1;
> -               }
> -       } else if (epause->tx_pause) {
> -               priv->tx_pause_en = 1;
> -       }
> -
> -       if (epause->autoneg)
> -               priv->pause_aneg_en = 1;
> -       else
> -               priv->pause_aneg_en = 0;
> -
> -       if (!epause->autoneg) {
> -               u32 tempval = gfar_read(&regs->maccfg1);
> -
> -               tempval &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
> -
> -               priv->tx_actual_en = 0;
> -               if (priv->tx_pause_en) {
> -                       priv->tx_actual_en = 1;
> -                       tempval |= MACCFG1_TX_FLOW;
> -               }
> -
> -               if (priv->rx_pause_en)
> -                       tempval |= MACCFG1_RX_FLOW;
> -               gfar_write(&regs->maccfg1, tempval);
> -       }
> -
> -       return 0;
> +       return phylink_ethtool_set_pauseparam(priv->phylink, epause);
>  }
>
>  int gfar_set_features(struct net_device *dev, netdev_features_t features)
> @@ -1519,6 +1472,24 @@ static int gfar_get_ts_info(struct net_device *dev,
>         return 0;
>  }
>
> +/* Set link ksettings (phy address, speed) for ethtools */

ethtool, not ethtools. Also, I'm not quite sure what you mean by
setting the "phy address" with ethtool.

> +static int gfar_get_link_ksettings(struct net_device *dev,
> +                                  struct ethtool_link_ksettings *cmd)
> +{
> +       struct gfar_private *priv = netdev_priv(dev);
> +
> +       return phylink_ethtool_ksettings_get(priv->phylink, cmd);
> +}
> +
> +/* Get link ksettings for ethtools */
> +static int gfar_set_link_ksettings(struct net_device *dev,
> +                                  const struct ethtool_link_ksettings *cmd)
> +{
> +       struct gfar_private *priv = netdev_priv(dev);
> +
> +       return phylink_ethtool_ksettings_set(priv->phylink, cmd);
> +}
> +
>  const struct ethtool_ops gfar_ethtool_ops = {
>         .get_drvinfo = gfar_gdrvinfo,
>         .get_regs_len = gfar_reglen,
> @@ -1542,6 +1513,6 @@ const struct ethtool_ops gfar_ethtool_ops = {
>         .set_rxnfc = gfar_set_nfc,
>         .get_rxnfc = gfar_get_nfc,
>         .get_ts_info = gfar_get_ts_info,
> -       .get_link_ksettings = phy_ethtool_get_link_ksettings,
> -       .set_link_ksettings = phy_ethtool_set_link_ksettings,
> +       .get_link_ksettings = gfar_get_link_ksettings,
> +       .set_link_ksettings = gfar_set_link_ksettings,
>  };
> --
> 2.22.0
>

Thanks a lot for doing this!
-Vladimir

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/2] gianfar: convert to phylink
  2019-07-29 23:39   ` Vladimir Oltean
@ 2019-07-30 10:23     ` Russell King - ARM Linux admin
  2019-08-24 12:49       ` Vladimir Oltean
  2019-07-30 14:40     ` Arseny Solokha
  2019-09-04 13:53     ` Arseny Solokha
  2 siblings, 1 reply; 26+ messages in thread
From: Russell King - ARM Linux admin @ 2019-07-30 10:23 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Arseny Solokha, Claudiu Manoil, Ioana Ciornei, Andrew Lunn,
	netdev, Florian Fainelli

On Tue, Jul 30, 2019 at 02:39:58AM +0300, Vladimir Oltean wrote:
> To be honest I don't have a complete answer to that question. The
> literature recommends writing 0x01a0 to the MII_ADVERTISE (0x4)
> register of the MAC PCS for 1000Base-X, and 0x4001 for SGMII.

That looks entirely sane for both modes.

0x01a0 for 802.3z (1000BASE-X) is defined in 802.3 37.2.5.1.3 as:
	bit 5 - full duplex = 1
	bit 6 - half duplex = 0
	bit 7 - pause = 1
	bit 8 - asym_pause = 1

The description of the bits match the config word that is sent in the
link with the exception of bit 14, which is the acknowledgement bit.
Normally, in 802.3z, bit 14 will not be set in the transmitted config
word until we have received the config word from the other end of the
link.

For SGMII, 0x4001 is the acknowledgement code word, which is defined
in the SGMII spec as "tx_config_Reg[15:0] sent from the MAC to the
PHY" which requires:
	bit 0 - must be 1
	bit 1 .. 13, 15 - must be 0, reserved for future use
	bit 14 - must be 1 (auto-negotiation acknowledgement in 802.3z)

> The FMan driver which uses the TSEC MAC does exactly that:
> https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git/tree/drivers/net/ethernet/freescale/fman/fman_dtsec.c#n58
> However I can't seem to be able to trace down the definition of bit 14
> from 0x4001 - it's reserved in all the manuals I see. I have a hunch
> that it selects the format of the Config_Reg base page between
> 1000Base-X and SGMII.

It could be that is what bit 14 is being used for, or it could just be
that they've taken the values from the appropriate specs, and the
hardware behaves the same way irrespective of whether it is in SGMII
or 1000BASE-X mode.

> > +static int gfar_mac_link_state(struct phylink_config *config,
> > +                              struct phylink_link_state *state)
> > +{
> > +       if (state->interface == PHY_INTERFACE_MODE_SGMII ||
> > +           state->interface == PHY_INTERFACE_MODE_1000BASEX) {
> 
> What if you reduce the indentation level by 1 here, by just exiting if
> the interface mode is not SGMII?

It's only called for in-band negotiation modes anyway, so the above
protection probably doesn't gain much.

> > +               struct gfar_private *priv =
> > +                       netdev_priv(to_net_dev(config->dev));
> > +               u16 tbi_cr;
> > +
> > +               if (!priv->tbi_phy)
> > +                       return -ENODEV;
> > +
> > +               tbi_cr = phy_read(priv->tbi_phy, MII_TBI_CR);
> > +
> > +               state->duplex = !!(tbi_cr & TBI_CR_FULL_DUPLEX);
> 
> Woah there. Aren't you supposed to first ensure state->an_complete is
> ok, based on TBI_MII_Register_Set_SR[AN_Done]? There's also a
> Link_Status bit in that register that you could retrieve.

Indeed.

> > +               if ((tbi_cr & TBI_CR_SPEED_1000_MASK) == TBI_CR_SPEED_1000_MASK)
> > +                       state->speed = SPEED_1000;
> > +       }
> 
> See the Speed_Bit table from TBI_MII_Register_Set_ANLPBPA_SGMII for
> the link partner (aka SGMII PHY) advertisement. You have to do a
> logical-and between that and your own. Also please make sure you
> really are in SGMII AN and not 1000 Base-X when interpreting registers
> 4 & 5 as one way or another.

From what you've said above, yes, this needs to do exactly that.

> > -static noinline void gfar_update_link_state(struct gfar_private *priv)
> > +static void gfar_mac_config(struct phylink_config *config, unsigned int mode,
> > +                           const struct phylink_link_state *state)
> >  {
> > +       struct gfar_private *priv = netdev_priv(to_net_dev(config->dev));
> >         struct gfar __iomem *regs = priv->gfargrp[0].regs;
> > -       struct net_device *ndev = priv->ndev;
> > -       struct phy_device *phydev = ndev->phydev;
> > -       struct gfar_priv_rx_q *rx_queue = NULL;
> > -       int i;
> > +       u32 maccfg1, new_maccfg1;
> > +       u32 maccfg2, new_maccfg2;
> > +       u32 ecntrl, new_ecntrl;
> > +       u32 tx_flow, new_tx_flow;
> 
> Don't introduce new_ variables. Is there any issue if you
> unconditionally write to the MAC registers?

We do this in every driver, as mac_config() can be called with only a
small number of changes in the settings, and it is important not to
upset the MAC for minor updates.

An example of this is when we are in SGMII mode with an attached PHY.
SGMII will communicate the speed and duplex, but not the results of
the pause negotiation.  We read that from the attached PHY and report
it back by calling mac_config() - but at that point, we don't want to
cause the established link to bounce.

So, mac_config() should be implemented to avoid upsetting an already
established link where possible (unless the configuration items that
affect the link have changed.)

> > +static void gfar_mac_an_restart(struct phylink_config *config)
> > +{
> > +       /* Not supported */
> > +}
> 
> What about running gfar_configure_serdes again?

The intention here is to cause the 802.3z link to renegotiate...

> 
> > +
> > +static void gfar_mac_link_down(struct phylink_config *config, unsigned int mode,
> > +                              phy_interface_t interface)
> > +{
> > +       /* Not supported */
> > +}
> > +
> 
> What about disabling RX_EN and TX_EN from MACCFG1?
> 
> > +static void gfar_mac_link_up(struct phylink_config *config, unsigned int mode,
> > +                            phy_interface_t interface, struct phy_device *phy)
> > +{
> > +       /* Not supported */
> >  }
> >
> 
> What about enabling RX_EN and TX_EN from MACCFG1?

Note that both of these functions must still allow the link to be
established if we are using in-band negotiation - but they are
expected to start/stop the transmission of packets.

> > @@ -149,8 +148,13 @@ extern const char gfar_driver_version[];
> >  #define GFAR_SUPPORTED_GBIT SUPPORTED_1000baseT_Full
> >
> >  /* TBI register addresses */
> > +#define MII_TBI_CR             0x00
> >  #define MII_TBICON             0x11
> >
> > +/* TBI_CR register bit fields */
> > +#define TBI_CR_FULL_DUPLEX     0x0100
> > +#define TBI_CR_SPEED_1000_MASK 0x0040
> > +
> 
> I think BIT() definitions are preferred, even if that means you have
> to convert existing code first.

If MII_TBI_CR is the BMCR of the PCS, how about using the definitions
that we already have in the kernel:

BMCR_SPEED1000 is TBI_CR_SPEED_1000_MASK
BMCR_FULLDPLX is TBI_CR_FULL_DUPLEX

?

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
According to speedtest.net: 11.9Mbps down 500kbps up

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/2] gianfar: convert to phylink
  2019-07-29 23:39   ` Vladimir Oltean
  2019-07-30 10:23     ` Russell King - ARM Linux admin
@ 2019-07-30 14:40     ` Arseny Solokha
  2019-08-24 15:21       ` Vladimir Oltean
  2019-09-04 13:53     ` Arseny Solokha
  2 siblings, 1 reply; 26+ messages in thread
From: Arseny Solokha @ 2019-07-30 14:40 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Claudiu Manoil, Russell King, Ioana Ciornei, Andrew Lunn, netdev,
	Florian Fainelli

> Hi Arseny,
>
> Nice project!

Vladimir, Russell, thanks for your review. I'm on vacation now, so won't fully
address your comments in a few weeks: while I can build the code, I won't have
access to hardware to test.

So it seems this patch will turn into a series where we'll have some cleanup
patches preceding the actual conversion (and the latter will also contain a
documentation change in Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
which I've overlooked in the first submission). I'll try to post trivial
cleanups independently while still on vacation.


>> @@ -891,11 +912,21 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
>>
>>         err = of_property_read_string(np, "phy-connection-type", &ctype);
>>
>> -       /* We only care about rgmii-id.  The rest are autodetected */
>> -       if (err == 0 && !strcmp(ctype, "rgmii-id"))
>> -               priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
>> -       else
>> +       /* We only care about rgmii-id and sgmii - the former
>> +        * is indistinguishable from rgmii in hardware, and phylink needs
>> +        * the latter to be set appropriately for correct phy configuration.
>> +        * The rest are autodetected
>> +        */
>> +       if (err == 0) {
>> +               if (!strcmp(ctype, "rgmii-id"))
>> +                       priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
>> +               else if (!strcmp(ctype, "sgmii"))
>> +                       priv->interface = PHY_INTERFACE_MODE_SGMII;
>> +               else
>> +                       priv->interface = PHY_INTERFACE_MODE_MII;
>> +       } else {
>>                 priv->interface = PHY_INTERFACE_MODE_MII;
>> +       }
>>
>
> No. Don't do this. Just do:
>
>     err = of_get_phy_mode(np);
>     if (err < 0)
>         goto err_grp_init;
>
>     priv->interface = err;
>
>>         if (of_find_property(np, "fsl,magic-packet", NULL))
>>                 priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
>> @@ -903,19 +934,21 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
>>         if (of_get_property(np, "fsl,wake-on-filer", NULL))
>>                 priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER;
>>
>> -       priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
>> +       priv->device_node = np;
>> +       priv->speed = SPEED_UNKNOWN;
>>
>> -       /* In the case of a fixed PHY, the DT node associated
>> -        * to the PHY is the Ethernet MAC DT node.
>> -        */
>> -       if (!priv->phy_node && of_phy_is_fixed_link(np)) {
>> -               err = of_phy_register_fixed_link(np);
>> -               if (err)
>> -                       goto err_grp_init;
>> +       priv->phylink_config.dev = &priv->ndev->dev;
>> +       priv->phylink_config.type = PHYLINK_NETDEV;
>>
>> -               priv->phy_node = of_node_get(np);
>> +       phylink = phylink_create(&priv->phylink_config, of_fwnode_handle(np),
>> +                                priv->interface, &gfar_phylink_ops);
>
> You introduced a bug here.
> of_phy_connect used to take the PHY interface type (for good or bad)
> from gfar_get_interface() (which is reconstructing it from the MAC
> registers).
> You are now passing the PHY interface type to phylink_create from the
> "phy-connection-type" DT property.
> At the very least, you are breaking LS1021A which uses phy-mode
> instead of phy-connection-type (hence my comment above to use the
> generic OF helper).
> Actually I think you just uncovered a latent bug, in that the DT
> bindings for phy-mode didn't mean much at all to the driver - it would
> rely on what the bootloader had set up.
> Actually DT bindings for phy-connection-type were most likely simply
> bolt on on top of gianfar when they figured they couldn't just
> auto-detect the various species of required RGMII delays.
> But gfar_get_interface is a piece of history that was introduced in
> the same commit as the enum phy_interface_t itself: e8a2b6a42073
> ("[PATCH] PHY: Add support for configuring the PHY connection
> interface"). Its time has come.

<…>

>>         }
>>
>> +       priv->tbi_phy = NULL;
>> +       interface = gfar_get_interface(dev);
>
> Be consistent and just go for priv->interface. Nobody's changing it anyway.

So if I get you right, I'm supposed to drop gfar_get_interface() and rely on DT
bindings entirely?


>> @@ -3387,23 +3384,6 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id)
>>         return IRQ_HANDLED;
>>  }
>>
>> -/* Called every time the controller might need to be made
>> - * aware of new link state.  The PHY code conveys this
>> - * information through variables in the phydev structure, and this
>> - * function converts those variables into the appropriate
>> - * register values, and can bring down the device if needed.
>> - */
>> -static void adjust_link(struct net_device *dev)
>> -{
>> -       struct gfar_private *priv = netdev_priv(dev);
>> -       struct phy_device *phydev = dev->phydev;
>> -
>> -       if (unlikely(phydev->link != priv->oldlink ||
>> -                    (phydev->link && (phydev->duplex != priv->oldduplex ||
>> -                                      phydev->speed != priv->oldspeed))))
>> -               gfar_update_link_state(priv);
>> -}
>
> Getting rid of the cruft from this function deserves its own patch.

How am I supposed to remove it without breaking the PHYLIB-based driver? Or do
you mean making it call gfar_update_link_state() just before the conversion
which will then remove adjust_link() altogether?


>>
>>         if (unlikely(test_bit(GFAR_RESETTING, &priv->state)))
>>                 return;
>>
>> -       if (phydev->link) {
>> -               u32 tempval1 = gfar_read(&regs->maccfg1);
>> -               u32 tempval = gfar_read(&regs->maccfg2);
>> -               u32 ecntrl = gfar_read(&regs->ecntrl);
>> -               u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW);
>> +       if (unlikely(phylink_autoneg_inband(mode)))
>> +               return;
>>
>> -               if (phydev->duplex != priv->oldduplex) {
>> -                       if (!(phydev->duplex))
>> -                               tempval &= ~(MACCFG2_FULL_DUPLEX);
>> -                       else
>> -                               tempval |= MACCFG2_FULL_DUPLEX;
>> +       maccfg1 = gfar_read(&regs->maccfg1);
>> +       maccfg2 = gfar_read(&regs->maccfg2);
>> +       ecntrl = gfar_read(&regs->ecntrl);
>>
>> -                       priv->oldduplex = phydev->duplex;
>> -               }
>> +       new_maccfg2 = maccfg2 & ~(MACCFG2_FULL_DUPLEX | MACCFG2_IF);
>> +       new_ecntrl = ecntrl & ~ECNTRL_R100;
>>
>> -               if (phydev->speed != priv->oldspeed) {
>> -                       switch (phydev->speed) {
>> -                       case 1000:
>> -                               tempval =
>> -                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
>> +       if (state->duplex)
>> +               new_maccfg2 |= MACCFG2_FULL_DUPLEX;
>>
>> -                               ecntrl &= ~(ECNTRL_R100);
>> -                               break;
>> -                       case 100:
>> -                       case 10:
>> -                               tempval =
>> -                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
>> -
>> -                               /* Reduced mode distinguishes
>> -                                * between 10 and 100
>> -                                */
>> -                               if (phydev->speed == SPEED_100)
>> -                                       ecntrl |= ECNTRL_R100;
>> -                               else
>> -                                       ecntrl &= ~(ECNTRL_R100);
>> -                               break;
>> -                       default:
>> -                               netif_warn(priv, link, priv->ndev,
>> -                                          "Ack!  Speed (%d) is not 10/100/1000!\n",
>> -                                          phydev->speed);
>
> Please 1. remove "Ack!" 2. treat SPEED_UNKNOWN here by setting the MAC
> into a low-power state (e.g. 10 Mbps - the power savings are real).
> Don't print that Speed -1 is not 10/100/1000, we know that.

In my first conversion attempt I see "Ack!" when changing link speed on when
shutting it down, so switching to 10 Mbps doesn't seem right for me—hence early
return in this case. Maybe I'm doing something wrong here.


>> diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
>> index 3433b46b90c1..146b30d07789 100644
>> --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
>> +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
>> @@ -35,7 +35,7 @@
>>  #include <asm/types.h>
>>  #include <linux/ethtool.h>
>>  #include <linux/mii.h>
>> -#include <linux/phy.h>
>> +#include <linux/phylink.h>
>>  #include <linux/sort.h>
>>  #include <linux/if_vlan.h>
>>  #include <linux/of_platform.h>
>> @@ -207,12 +207,10 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs,
>>  static unsigned int gfar_usecs2ticks(struct gfar_private *priv,
>>                                      unsigned int usecs)
>>  {
>> -       struct net_device *ndev = priv->ndev;
>> -       struct phy_device *phydev = ndev->phydev;
>
> Are you sure this still works? You missed a ndev->phydev check from
> gfar_gcoalesce, where this is called from. Technically you can still
> check ndev->phydev, it's just that PHYLINK doesn't guarantee you'll
> have one (e.g. fixed-link interfaces).

It still works for RGMII PHYs, SGMII and 1000Base-X in my testing. I didn't
check it with fixed-link, though.


>> @@ -1519,6 +1472,24 @@ static int gfar_get_ts_info(struct net_device *dev,
>>         return 0;
>>  }
>>
>> +/* Set link ksettings (phy address, speed) for ethtools */
>
> ethtool, not ethtools. Also, I'm not quite sure what you mean by
> setting the "phy address" with ethtool.

Well, I know where I've copied it from… Thanks for pointing it out.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/2] gianfar: convert to phylink
  2019-07-30 10:23     ` Russell King - ARM Linux admin
@ 2019-08-24 12:49       ` Vladimir Oltean
  0 siblings, 0 replies; 26+ messages in thread
From: Vladimir Oltean @ 2019-08-24 12:49 UTC (permalink / raw)
  To: Russell King - ARM Linux admin
  Cc: Arseny Solokha, Claudiu Manoil, Ioana Ciornei, Andrew Lunn,
	netdev, Florian Fainelli

Hi Russell,

On Tue, 30 Jul 2019 at 13:23, Russell King - ARM Linux admin
<linux@armlinux.org.uk> wrote:
>
> On Tue, Jul 30, 2019 at 02:39:58AM +0300, Vladimir Oltean wrote:
> > To be honest I don't have a complete answer to that question. The
> > literature recommends writing 0x01a0 to the MII_ADVERTISE (0x4)
> > register of the MAC PCS for 1000Base-X, and 0x4001 for SGMII.
>
> That looks entirely sane for both modes.
>
> 0x01a0 for 802.3z (1000BASE-X) is defined in 802.3 37.2.5.1.3 as:
>         bit 5 - full duplex = 1
>         bit 6 - half duplex = 0
>         bit 7 - pause = 1
>         bit 8 - asym_pause = 1
>
> The description of the bits match the config word that is sent in the
> link with the exception of bit 14, which is the acknowledgement bit.
> Normally, in 802.3z, bit 14 will not be set in the transmitted config
> word until we have received the config word from the other end of the
> link.
>
> For SGMII, 0x4001 is the acknowledgement code word, which is defined
> in the SGMII spec as "tx_config_Reg[15:0] sent from the MAC to the
> PHY" which requires:
>         bit 0 - must be 1
>         bit 1 .. 13, 15 - must be 0, reserved for future use
>         bit 14 - must be 1 (auto-negotiation acknowledgement in 802.3z)
>
> > The FMan driver which uses the TSEC MAC does exactly that:
> > https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git/tree/drivers/net/ethernet/freescale/fman/fman_dtsec.c#n58
> > However I can't seem to be able to trace down the definition of bit 14
> > from 0x4001 - it's reserved in all the manuals I see. I have a hunch
> > that it selects the format of the Config_Reg base page between
> > 1000Base-X and SGMII.
>
> It could be that is what bit 14 is being used for, or it could just be
> that they've taken the values from the appropriate specs, and the
> hardware behaves the same way irrespective of whether it is in SGMII
> or 1000BASE-X mode.
>

Mystery solved - indeed it appears that there is no hardware logic for
propagating the AN results from the PCS to the MAC. Hence there is no
hardware distinction between 1000Base-X and SGMII. That must be
handled in PHYLINK.

> > > +static int gfar_mac_link_state(struct phylink_config *config,
> > > +                              struct phylink_link_state *state)
> > > +{
> > > +       if (state->interface == PHY_INTERFACE_MODE_SGMII ||
> > > +           state->interface == PHY_INTERFACE_MODE_1000BASEX) {
> >
> > What if you reduce the indentation level by 1 here, by just exiting if
> > the interface mode is not SGMII?
>
> It's only called for in-band negotiation modes anyway, so the above
> protection probably doesn't gain much.
>

Or that.

> > > +               struct gfar_private *priv =
> > > +                       netdev_priv(to_net_dev(config->dev));
> > > +               u16 tbi_cr;
> > > +
> > > +               if (!priv->tbi_phy)
> > > +                       return -ENODEV;
> > > +
> > > +               tbi_cr = phy_read(priv->tbi_phy, MII_TBI_CR);
> > > +
> > > +               state->duplex = !!(tbi_cr & TBI_CR_FULL_DUPLEX);
> >
> > Woah there. Aren't you supposed to first ensure state->an_complete is
> > ok, based on TBI_MII_Register_Set_SR[AN_Done]? There's also a
> > Link_Status bit in that register that you could retrieve.
>
> Indeed.
>
> > > +               if ((tbi_cr & TBI_CR_SPEED_1000_MASK) == TBI_CR_SPEED_1000_MASK)
> > > +                       state->speed = SPEED_1000;
> > > +       }
> >
> > See the Speed_Bit table from TBI_MII_Register_Set_ANLPBPA_SGMII for
> > the link partner (aka SGMII PHY) advertisement. You have to do a
> > logical-and between that and your own. Also please make sure you
> > really are in SGMII AN and not 1000 Base-X when interpreting registers
> > 4 & 5 as one way or another.
>
> From what you've said above, yes, this needs to do exactly that.
>
> > > -static noinline void gfar_update_link_state(struct gfar_private *priv)
> > > +static void gfar_mac_config(struct phylink_config *config, unsigned int mode,
> > > +                           const struct phylink_link_state *state)
> > >  {
> > > +       struct gfar_private *priv = netdev_priv(to_net_dev(config->dev));
> > >         struct gfar __iomem *regs = priv->gfargrp[0].regs;
> > > -       struct net_device *ndev = priv->ndev;
> > > -       struct phy_device *phydev = ndev->phydev;
> > > -       struct gfar_priv_rx_q *rx_queue = NULL;
> > > -       int i;
> > > +       u32 maccfg1, new_maccfg1;
> > > +       u32 maccfg2, new_maccfg2;
> > > +       u32 ecntrl, new_ecntrl;
> > > +       u32 tx_flow, new_tx_flow;
> >
> > Don't introduce new_ variables. Is there any issue if you
> > unconditionally write to the MAC registers?
>
> We do this in every driver, as mac_config() can be called with only a
> small number of changes in the settings, and it is important not to
> upset the MAC for minor updates.
>
> An example of this is when we are in SGMII mode with an attached PHY.
> SGMII will communicate the speed and duplex, but not the results of
> the pause negotiation.  We read that from the attached PHY and report
> it back by calling mac_config() - but at that point, we don't want to
> cause the established link to bounce.
>
> So, mac_config() should be implemented to avoid upsetting an already
> established link where possible (unless the configuration items that
> affect the link have changed.)
>

Ok.

> > > +static void gfar_mac_an_restart(struct phylink_config *config)
> > > +{
> > > +       /* Not supported */
> > > +}
> >
> > What about running gfar_configure_serdes again?
>
> The intention here is to cause the 802.3z link to renegotiate...
>

Yes, gfar_configure_serdes does this:

    phy_write(tbiphy, MII_BMCR,
          BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX |
          BMCR_SPEED1000);

> >
> > > +
> > > +static void gfar_mac_link_down(struct phylink_config *config, unsigned int mode,
> > > +                              phy_interface_t interface)
> > > +{
> > > +       /* Not supported */
> > > +}
> > > +
> >
> > What about disabling RX_EN and TX_EN from MACCFG1?
> >
> > > +static void gfar_mac_link_up(struct phylink_config *config, unsigned int mode,
> > > +                            phy_interface_t interface, struct phy_device *phy)
> > > +{
> > > +       /* Not supported */
> > >  }
> > >
> >
> > What about enabling RX_EN and TX_EN from MACCFG1?
>
> Note that both of these functions must still allow the link to be
> established if we are using in-band negotiation - but they are
> expected to start/stop the transmission of packets.
>

I don't think AN pages count as Ethernet frames, and the RX_EN and
TX_EN bits are MAC settings anyway - it is the PCS below it that would
process them.

> > > @@ -149,8 +148,13 @@ extern const char gfar_driver_version[];
> > >  #define GFAR_SUPPORTED_GBIT SUPPORTED_1000baseT_Full
> > >
> > >  /* TBI register addresses */
> > > +#define MII_TBI_CR             0x00
> > >  #define MII_TBICON             0x11
> > >
> > > +/* TBI_CR register bit fields */
> > > +#define TBI_CR_FULL_DUPLEX     0x0100
> > > +#define TBI_CR_SPEED_1000_MASK 0x0040
> > > +
> >
> > I think BIT() definitions are preferred, even if that means you have
> > to convert existing code first.
>
> If MII_TBI_CR is the BMCR of the PCS, how about using the definitions
> that we already have in the kernel:
>
> BMCR_SPEED1000 is TBI_CR_SPEED_1000_MASK
> BMCR_FULLDPLX is TBI_CR_FULL_DUPLEX
>
> ?

Or that, yes.

>
> --
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
> According to speedtest.net: 11.9Mbps down 500kbps up

Thanks,
-Vladimir

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/2] gianfar: convert to phylink
  2019-07-30 14:40     ` Arseny Solokha
@ 2019-08-24 15:21       ` Vladimir Oltean
  2019-08-28 15:20         ` Vladimir Oltean
                           ` (2 more replies)
  0 siblings, 3 replies; 26+ messages in thread
From: Vladimir Oltean @ 2019-08-24 15:21 UTC (permalink / raw)
  To: Arseny Solokha
  Cc: Claudiu Manoil, Russell King, Ioana Ciornei, Andrew Lunn, netdev,
	Florian Fainelli

Hi Arseny.

On Tue, 30 Jul 2019 at 17:40, Arseny Solokha <asolokha@kb.kras.ru> wrote:
>
> > Hi Arseny,
> >
> > Nice project!
>
> Vladimir, Russell, thanks for your review. I'm on vacation now, so won't fully
> address your comments in a few weeks: while I can build the code, I won't have
> access to hardware to test.
>
> So it seems this patch will turn into a series where we'll have some cleanup
> patches preceding the actual conversion (and the latter will also contain a
> documentation change in Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
> which I've overlooked in the first submission). I'll try to post trivial
> cleanups independently while still on vacation.
>

Yes, ideally the cleanup would be separate from the conversion.

>
> >> @@ -891,11 +912,21 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
> >>
> >>         err = of_property_read_string(np, "phy-connection-type", &ctype);
> >>
> >> -       /* We only care about rgmii-id.  The rest are autodetected */
> >> -       if (err == 0 && !strcmp(ctype, "rgmii-id"))
> >> -               priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
> >> -       else
> >> +       /* We only care about rgmii-id and sgmii - the former
> >> +        * is indistinguishable from rgmii in hardware, and phylink needs
> >> +        * the latter to be set appropriately for correct phy configuration.
> >> +        * The rest are autodetected
> >> +        */
> >> +       if (err == 0) {
> >> +               if (!strcmp(ctype, "rgmii-id"))
> >> +                       priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
> >> +               else if (!strcmp(ctype, "sgmii"))
> >> +                       priv->interface = PHY_INTERFACE_MODE_SGMII;
> >> +               else
> >> +                       priv->interface = PHY_INTERFACE_MODE_MII;
> >> +       } else {
> >>                 priv->interface = PHY_INTERFACE_MODE_MII;
> >> +       }
> >>
> >
> > No. Don't do this. Just do:
> >
> >     err = of_get_phy_mode(np);
> >     if (err < 0)
> >         goto err_grp_init;
> >
> >     priv->interface = err;
> >
> >>         if (of_find_property(np, "fsl,magic-packet", NULL))
> >>                 priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
> >> @@ -903,19 +934,21 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
> >>         if (of_get_property(np, "fsl,wake-on-filer", NULL))
> >>                 priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER;
> >>
> >> -       priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
> >> +       priv->device_node = np;
> >> +       priv->speed = SPEED_UNKNOWN;
> >>
> >> -       /* In the case of a fixed PHY, the DT node associated
> >> -        * to the PHY is the Ethernet MAC DT node.
> >> -        */
> >> -       if (!priv->phy_node && of_phy_is_fixed_link(np)) {
> >> -               err = of_phy_register_fixed_link(np);
> >> -               if (err)
> >> -                       goto err_grp_init;
> >> +       priv->phylink_config.dev = &priv->ndev->dev;
> >> +       priv->phylink_config.type = PHYLINK_NETDEV;
> >>
> >> -               priv->phy_node = of_node_get(np);
> >> +       phylink = phylink_create(&priv->phylink_config, of_fwnode_handle(np),
> >> +                                priv->interface, &gfar_phylink_ops);
> >
> > You introduced a bug here.
> > of_phy_connect used to take the PHY interface type (for good or bad)
> > from gfar_get_interface() (which is reconstructing it from the MAC
> > registers).
> > You are now passing the PHY interface type to phylink_create from the
> > "phy-connection-type" DT property.
> > At the very least, you are breaking LS1021A which uses phy-mode
> > instead of phy-connection-type (hence my comment above to use the
> > generic OF helper).
> > Actually I think you just uncovered a latent bug, in that the DT
> > bindings for phy-mode didn't mean much at all to the driver - it would
> > rely on what the bootloader had set up.
> > Actually DT bindings for phy-connection-type were most likely simply
> > bolt on on top of gianfar when they figured they couldn't just
> > auto-detect the various species of required RGMII delays.
> > But gfar_get_interface is a piece of history that was introduced in
> > the same commit as the enum phy_interface_t itself: e8a2b6a42073
> > ("[PATCH] PHY: Add support for configuring the PHY connection
> > interface"). Its time has come.
>
> <…>
>
> >>         }
> >>
> >> +       priv->tbi_phy = NULL;
> >> +       interface = gfar_get_interface(dev);
> >
> > Be consistent and just go for priv->interface. Nobody's changing it anyway.
>
> So if I get you right, I'm supposed to drop gfar_get_interface() and rely on DT
> bindings entirely?
>

Oof, I checked arch/powerpc/boot/dts/fsl and the following boards
using the gianfar driver are guilty of populating phy-handle but not
phy-connection-type:
mpc8540ads
mpc8541cds
mpc8548cds_32b
mpc8548cds_36b
mpc8555cds
mpc8560ads
mpc8568mds
ppa8548

I think a sane logic for populating priv->interface would be:
- Attempt of_get_phy_mode
- If phy-mode or phy-connection-type properties are not found, revert
to gfar_get_interface for the legacy blobs above.

>
> >> @@ -3387,23 +3384,6 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id)
> >>         return IRQ_HANDLED;
> >>  }
> >>
> >> -/* Called every time the controller might need to be made
> >> - * aware of new link state.  The PHY code conveys this
> >> - * information through variables in the phydev structure, and this
> >> - * function converts those variables into the appropriate
> >> - * register values, and can bring down the device if needed.
> >> - */
> >> -static void adjust_link(struct net_device *dev)
> >> -{
> >> -       struct gfar_private *priv = netdev_priv(dev);
> >> -       struct phy_device *phydev = dev->phydev;
> >> -
> >> -       if (unlikely(phydev->link != priv->oldlink ||
> >> -                    (phydev->link && (phydev->duplex != priv->oldduplex ||
> >> -                                      phydev->speed != priv->oldspeed))))
> >> -               gfar_update_link_state(priv);
> >> -}
> >
> > Getting rid of the cruft from this function deserves its own patch.
>
> How am I supposed to remove it without breaking the PHYLIB-based driver? Or do
> you mean making it call gfar_update_link_state() just before the conversion
> which will then remove adjust_link() altogether?
>

I don't know, if you can't refactor without breaking anything then ok.

>
> >>
> >>         if (unlikely(test_bit(GFAR_RESETTING, &priv->state)))
> >>                 return;
> >>
> >> -       if (phydev->link) {
> >> -               u32 tempval1 = gfar_read(&regs->maccfg1);
> >> -               u32 tempval = gfar_read(&regs->maccfg2);
> >> -               u32 ecntrl = gfar_read(&regs->ecntrl);
> >> -               u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW);
> >> +       if (unlikely(phylink_autoneg_inband(mode)))
> >> +               return;
> >>
> >> -               if (phydev->duplex != priv->oldduplex) {
> >> -                       if (!(phydev->duplex))
> >> -                               tempval &= ~(MACCFG2_FULL_DUPLEX);
> >> -                       else
> >> -                               tempval |= MACCFG2_FULL_DUPLEX;
> >> +       maccfg1 = gfar_read(&regs->maccfg1);
> >> +       maccfg2 = gfar_read(&regs->maccfg2);
> >> +       ecntrl = gfar_read(&regs->ecntrl);
> >>
> >> -                       priv->oldduplex = phydev->duplex;
> >> -               }
> >> +       new_maccfg2 = maccfg2 & ~(MACCFG2_FULL_DUPLEX | MACCFG2_IF);
> >> +       new_ecntrl = ecntrl & ~ECNTRL_R100;
> >>
> >> -               if (phydev->speed != priv->oldspeed) {
> >> -                       switch (phydev->speed) {
> >> -                       case 1000:
> >> -                               tempval =
> >> -                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
> >> +       if (state->duplex)
> >> +               new_maccfg2 |= MACCFG2_FULL_DUPLEX;
> >>
> >> -                               ecntrl &= ~(ECNTRL_R100);
> >> -                               break;
> >> -                       case 100:
> >> -                       case 10:
> >> -                               tempval =
> >> -                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
> >> -
> >> -                               /* Reduced mode distinguishes
> >> -                                * between 10 and 100
> >> -                                */
> >> -                               if (phydev->speed == SPEED_100)
> >> -                                       ecntrl |= ECNTRL_R100;
> >> -                               else
> >> -                                       ecntrl &= ~(ECNTRL_R100);
> >> -                               break;
> >> -                       default:
> >> -                               netif_warn(priv, link, priv->ndev,
> >> -                                          "Ack!  Speed (%d) is not 10/100/1000!\n",
> >> -                                          phydev->speed);
> >
> > Please 1. remove "Ack!" 2. treat SPEED_UNKNOWN here by setting the MAC
> > into a low-power state (e.g. 10 Mbps - the power savings are real).
> > Don't print that Speed -1 is not 10/100/1000, we know that.
>
> In my first conversion attempt I see "Ack!" when changing link speed on when
> shutting it down, so switching to 10 Mbps doesn't seem right for me—hence early
> return in this case. Maybe I'm doing something wrong here.
>

When mac_config calls with SPEED_UNKNOWN, the link is down, and you
can put the MAC in the lowest energy state it can go to (10 Mbps, in
this case). Or so I've been told. Maybe Russell can chime in. Anyway,
you don't need to print anything, there's lots of prints from PHYLINK
already.

>
> >> diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
> >> index 3433b46b90c1..146b30d07789 100644
> >> --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
> >> +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
> >> @@ -35,7 +35,7 @@
> >>  #include <asm/types.h>
> >>  #include <linux/ethtool.h>
> >>  #include <linux/mii.h>
> >> -#include <linux/phy.h>
> >> +#include <linux/phylink.h>
> >>  #include <linux/sort.h>
> >>  #include <linux/if_vlan.h>
> >>  #include <linux/of_platform.h>
> >> @@ -207,12 +207,10 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs,
> >>  static unsigned int gfar_usecs2ticks(struct gfar_private *priv,
> >>                                      unsigned int usecs)
> >>  {
> >> -       struct net_device *ndev = priv->ndev;
> >> -       struct phy_device *phydev = ndev->phydev;
> >
> > Are you sure this still works? You missed a ndev->phydev check from
> > gfar_gcoalesce, where this is called from. Technically you can still
> > check ndev->phydev, it's just that PHYLINK doesn't guarantee you'll
> > have one (e.g. fixed-link interfaces).
>
> It still works for RGMII PHYs, SGMII and 1000Base-X in my testing. I didn't
> check it with fixed-link, though.
>
>
> >> @@ -1519,6 +1472,24 @@ static int gfar_get_ts_info(struct net_device *dev,
> >>         return 0;
> >>  }
> >>
> >> +/* Set link ksettings (phy address, speed) for ethtools */
> >
> > ethtool, not ethtools. Also, I'm not quite sure what you mean by
> > setting the "phy address" with ethtool.
>
> Well, I know where I've copied it from… Thanks for pointing it out.

Regards,
-Vladimir

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/2] gianfar: convert to phylink
  2019-08-24 15:21       ` Vladimir Oltean
@ 2019-08-28 15:20         ` Vladimir Oltean
  2019-09-04 13:52         ` [PATCH 0/4] gianfar: some assorted cleanup Arseny Solokha
  2019-09-04 13:54         ` [RFC PATCH 1/2] gianfar: convert to phylink Arseny Solokha
  2 siblings, 0 replies; 26+ messages in thread
From: Vladimir Oltean @ 2019-08-28 15:20 UTC (permalink / raw)
  To: Arseny Solokha
  Cc: Claudiu Manoil, Russell King, Ioana Ciornei, Andrew Lunn, netdev,
	Florian Fainelli

Hi Arseny,

On Sat, 24 Aug 2019 at 18:21, Vladimir Oltean <olteanv@gmail.com> wrote:
>
> Hi Arseny.
>
> On Tue, 30 Jul 2019 at 17:40, Arseny Solokha <asolokha@kb.kras.ru> wrote:
> >
> > > Hi Arseny,
> > >
> > > Nice project!
> >
> > Vladimir, Russell, thanks for your review. I'm on vacation now, so won't fully
> > address your comments in a few weeks: while I can build the code, I won't have
> > access to hardware to test.
> >
> > So it seems this patch will turn into a series where we'll have some cleanup
> > patches preceding the actual conversion (and the latter will also contain a
> > documentation change in Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
> > which I've overlooked in the first submission). I'll try to post trivial
> > cleanups independently while still on vacation.
> >
>
> Yes, ideally the cleanup would be separate from the conversion.
>
> >
> > >> @@ -891,11 +912,21 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
> > >>
> > >>         err = of_property_read_string(np, "phy-connection-type", &ctype);
> > >>
> > >> -       /* We only care about rgmii-id.  The rest are autodetected */
> > >> -       if (err == 0 && !strcmp(ctype, "rgmii-id"))
> > >> -               priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
> > >> -       else
> > >> +       /* We only care about rgmii-id and sgmii - the former
> > >> +        * is indistinguishable from rgmii in hardware, and phylink needs
> > >> +        * the latter to be set appropriately for correct phy configuration.
> > >> +        * The rest are autodetected
> > >> +        */
> > >> +       if (err == 0) {
> > >> +               if (!strcmp(ctype, "rgmii-id"))
> > >> +                       priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
> > >> +               else if (!strcmp(ctype, "sgmii"))
> > >> +                       priv->interface = PHY_INTERFACE_MODE_SGMII;
> > >> +               else
> > >> +                       priv->interface = PHY_INTERFACE_MODE_MII;
> > >> +       } else {
> > >>                 priv->interface = PHY_INTERFACE_MODE_MII;
> > >> +       }
> > >>
> > >
> > > No. Don't do this. Just do:
> > >
> > >     err = of_get_phy_mode(np);
> > >     if (err < 0)
> > >         goto err_grp_init;
> > >
> > >     priv->interface = err;
> > >
> > >>         if (of_find_property(np, "fsl,magic-packet", NULL))
> > >>                 priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
> > >> @@ -903,19 +934,21 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
> > >>         if (of_get_property(np, "fsl,wake-on-filer", NULL))
> > >>                 priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER;
> > >>
> > >> -       priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
> > >> +       priv->device_node = np;
> > >> +       priv->speed = SPEED_UNKNOWN;
> > >>
> > >> -       /* In the case of a fixed PHY, the DT node associated
> > >> -        * to the PHY is the Ethernet MAC DT node.
> > >> -        */
> > >> -       if (!priv->phy_node && of_phy_is_fixed_link(np)) {
> > >> -               err = of_phy_register_fixed_link(np);
> > >> -               if (err)
> > >> -                       goto err_grp_init;
> > >> +       priv->phylink_config.dev = &priv->ndev->dev;
> > >> +       priv->phylink_config.type = PHYLINK_NETDEV;
> > >>
> > >> -               priv->phy_node = of_node_get(np);
> > >> +       phylink = phylink_create(&priv->phylink_config, of_fwnode_handle(np),
> > >> +                                priv->interface, &gfar_phylink_ops);
> > >
> > > You introduced a bug here.
> > > of_phy_connect used to take the PHY interface type (for good or bad)
> > > from gfar_get_interface() (which is reconstructing it from the MAC
> > > registers).
> > > You are now passing the PHY interface type to phylink_create from the
> > > "phy-connection-type" DT property.
> > > At the very least, you are breaking LS1021A which uses phy-mode
> > > instead of phy-connection-type (hence my comment above to use the
> > > generic OF helper).
> > > Actually I think you just uncovered a latent bug, in that the DT
> > > bindings for phy-mode didn't mean much at all to the driver - it would
> > > rely on what the bootloader had set up.
> > > Actually DT bindings for phy-connection-type were most likely simply
> > > bolt on on top of gianfar when they figured they couldn't just
> > > auto-detect the various species of required RGMII delays.
> > > But gfar_get_interface is a piece of history that was introduced in
> > > the same commit as the enum phy_interface_t itself: e8a2b6a42073
> > > ("[PATCH] PHY: Add support for configuring the PHY connection
> > > interface"). Its time has come.
> >
> > <…>
> >
> > >>         }
> > >>
> > >> +       priv->tbi_phy = NULL;
> > >> +       interface = gfar_get_interface(dev);
> > >
> > > Be consistent and just go for priv->interface. Nobody's changing it anyway.
> >
> > So if I get you right, I'm supposed to drop gfar_get_interface() and rely on DT
> > bindings entirely?
> >
>
> Oof, I checked arch/powerpc/boot/dts/fsl and the following boards
> using the gianfar driver are guilty of populating phy-handle but not
> phy-connection-type:
> mpc8540ads
> mpc8541cds
> mpc8548cds_32b
> mpc8548cds_36b
> mpc8555cds
> mpc8560ads
> mpc8568mds
> ppa8548
>
> I think a sane logic for populating priv->interface would be:
> - Attempt of_get_phy_mode
> - If phy-mode or phy-connection-type properties are not found, revert
> to gfar_get_interface for the legacy blobs above.
>
> >
> > >> @@ -3387,23 +3384,6 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id)
> > >>         return IRQ_HANDLED;
> > >>  }
> > >>
> > >> -/* Called every time the controller might need to be made
> > >> - * aware of new link state.  The PHY code conveys this
> > >> - * information through variables in the phydev structure, and this
> > >> - * function converts those variables into the appropriate
> > >> - * register values, and can bring down the device if needed.
> > >> - */
> > >> -static void adjust_link(struct net_device *dev)
> > >> -{
> > >> -       struct gfar_private *priv = netdev_priv(dev);
> > >> -       struct phy_device *phydev = dev->phydev;
> > >> -
> > >> -       if (unlikely(phydev->link != priv->oldlink ||
> > >> -                    (phydev->link && (phydev->duplex != priv->oldduplex ||
> > >> -                                      phydev->speed != priv->oldspeed))))
> > >> -               gfar_update_link_state(priv);
> > >> -}
> > >
> > > Getting rid of the cruft from this function deserves its own patch.
> >
> > How am I supposed to remove it without breaking the PHYLIB-based driver? Or do
> > you mean making it call gfar_update_link_state() just before the conversion
> > which will then remove adjust_link() altogether?
> >
>
> I don't know, if you can't refactor without breaking anything then ok.
>
> >
> > >>
> > >>         if (unlikely(test_bit(GFAR_RESETTING, &priv->state)))
> > >>                 return;
> > >>
> > >> -       if (phydev->link) {
> > >> -               u32 tempval1 = gfar_read(&regs->maccfg1);
> > >> -               u32 tempval = gfar_read(&regs->maccfg2);
> > >> -               u32 ecntrl = gfar_read(&regs->ecntrl);
> > >> -               u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW);
> > >> +       if (unlikely(phylink_autoneg_inband(mode)))
> > >> +               return;
> > >>
> > >> -               if (phydev->duplex != priv->oldduplex) {
> > >> -                       if (!(phydev->duplex))
> > >> -                               tempval &= ~(MACCFG2_FULL_DUPLEX);
> > >> -                       else
> > >> -                               tempval |= MACCFG2_FULL_DUPLEX;
> > >> +       maccfg1 = gfar_read(&regs->maccfg1);
> > >> +       maccfg2 = gfar_read(&regs->maccfg2);
> > >> +       ecntrl = gfar_read(&regs->ecntrl);
> > >>
> > >> -                       priv->oldduplex = phydev->duplex;
> > >> -               }
> > >> +       new_maccfg2 = maccfg2 & ~(MACCFG2_FULL_DUPLEX | MACCFG2_IF);
> > >> +       new_ecntrl = ecntrl & ~ECNTRL_R100;
> > >>
> > >> -               if (phydev->speed != priv->oldspeed) {
> > >> -                       switch (phydev->speed) {
> > >> -                       case 1000:
> > >> -                               tempval =
> > >> -                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
> > >> +       if (state->duplex)
> > >> +               new_maccfg2 |= MACCFG2_FULL_DUPLEX;
> > >>
> > >> -                               ecntrl &= ~(ECNTRL_R100);
> > >> -                               break;
> > >> -                       case 100:
> > >> -                       case 10:
> > >> -                               tempval =
> > >> -                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
> > >> -
> > >> -                               /* Reduced mode distinguishes
> > >> -                                * between 10 and 100
> > >> -                                */
> > >> -                               if (phydev->speed == SPEED_100)
> > >> -                                       ecntrl |= ECNTRL_R100;
> > >> -                               else
> > >> -                                       ecntrl &= ~(ECNTRL_R100);
> > >> -                               break;
> > >> -                       default:
> > >> -                               netif_warn(priv, link, priv->ndev,
> > >> -                                          "Ack!  Speed (%d) is not 10/100/1000!\n",
> > >> -                                          phydev->speed);
> > >
> > > Please 1. remove "Ack!" 2. treat SPEED_UNKNOWN here by setting the MAC
> > > into a low-power state (e.g. 10 Mbps - the power savings are real).
> > > Don't print that Speed -1 is not 10/100/1000, we know that.
> >
> > In my first conversion attempt I see "Ack!" when changing link speed on when
> > shutting it down, so switching to 10 Mbps doesn't seem right for me—hence early
> > return in this case. Maybe I'm doing something wrong here.
> >
>
> When mac_config calls with SPEED_UNKNOWN, the link is down, and you
> can put the MAC in the lowest energy state it can go to (10 Mbps, in
> this case). Or so I've been told. Maybe Russell can chime in. Anyway,
> you don't need to print anything, there's lots of prints from PHYLINK
> already.
>
> >
> > >> diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
> > >> index 3433b46b90c1..146b30d07789 100644
> > >> --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
> > >> +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
> > >> @@ -35,7 +35,7 @@
> > >>  #include <asm/types.h>
> > >>  #include <linux/ethtool.h>
> > >>  #include <linux/mii.h>
> > >> -#include <linux/phy.h>
> > >> +#include <linux/phylink.h>
> > >>  #include <linux/sort.h>
> > >>  #include <linux/if_vlan.h>
> > >>  #include <linux/of_platform.h>
> > >> @@ -207,12 +207,10 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs,
> > >>  static unsigned int gfar_usecs2ticks(struct gfar_private *priv,
> > >>                                      unsigned int usecs)
> > >>  {
> > >> -       struct net_device *ndev = priv->ndev;
> > >> -       struct phy_device *phydev = ndev->phydev;
> > >
> > > Are you sure this still works? You missed a ndev->phydev check from
> > > gfar_gcoalesce, where this is called from. Technically you can still
> > > check ndev->phydev, it's just that PHYLINK doesn't guarantee you'll
> > > have one (e.g. fixed-link interfaces).
> >
> > It still works for RGMII PHYs, SGMII and 1000Base-X in my testing. I didn't
> > check it with fixed-link, though.
> >
> >
> > >> @@ -1519,6 +1472,24 @@ static int gfar_get_ts_info(struct net_device *dev,
> > >>         return 0;
> > >>  }
> > >>
> > >> +/* Set link ksettings (phy address, speed) for ethtools */
> > >
> > > ethtool, not ethtools. Also, I'm not quite sure what you mean by
> > > setting the "phy address" with ethtool.
> >
> > Well, I know where I've copied it from… Thanks for pointing it out.
>
> Regards,
> -Vladimir

Coming back to your patch.
Here is a gfar_reset_task gone horribly wrong, due to it not holding
rtnl_lock while calling phylink_stop:

[ 4115.081902] ------------[ cut here ]------------
[ 4115.086544] WARNING: CPU: 1 PID: 0 at net/sched/sch_generic.c:443
dev_watchdog+0x2ec/0x2f0
[ 4115.094802] NETDEV WATCHDOG: eth2 (fsl-gianfar): transmit queue 0 timed out
[ 4115.101727] Modules linked in:
[ 4115.104794] CPU: 1 PID: 0 Comm: swapper/1 Not tainted
5.3.0-rc6-01275-g246284a4920a-dirty #251
[ 4115.113360] Hardware name: Freescale LS1021A
[ 4115.117635] [<c03132e0>] (unwind_backtrace) from [<c030d8b8>]
(show_stack+0x10/0x14)
[ 4115.125351] [<c030d8b8>] (show_stack) from [<c10b41f0>]
(dump_stack+0xb0/0xc4)
[ 4115.132550] [<c10b41f0>] (dump_stack) from [<c0349dc8>] (__warn+0xf8/0x110)
[ 4115.139488] [<c0349dc8>] (__warn) from [<c0349e24>]
(warn_slowpath_fmt+0x44/0x68)
[ 4115.146942] [<c0349e24>] (warn_slowpath_fmt) from [<c0f28a4c>]
(dev_watchdog+0x2ec/0x2f0)
[ 4115.155089] [<c0f28a4c>] (dev_watchdog) from [<c03c2800>]
(call_timer_fn+0x3c/0x18c)
[ 4115.162806] [<c03c2800>] (call_timer_fn) from [<c03c2a2c>]
(expire_timers+0xdc/0x144)
[ 4115.170608] [<c03c2a2c>] (expire_timers) from [<c03c2d38>]
(run_timer_softirq+0xac/0x1d4)
[ 4115.178754] [<c03c2d38>] (run_timer_softirq) from [<c030221c>]
(__do_softirq+0xb4/0x364)
[ 4115.186815] [<c030221c>] (__do_softirq) from [<c03502c0>]
(irq_exit+0xbc/0xd8)
[ 4115.194012] [<c03502c0>] (irq_exit) from [<c03a426c>]
(__handle_domain_irq+0x60/0xb4)
[ 4115.201818] [<c03a426c>] (__handle_domain_irq) from [<c0754260>]
(gic_handle_irq+0x58/0x9c)
[ 4115.210139] [<c0754260>] (gic_handle_irq) from [<c0301a8c>]
(__irq_svc+0x6c/0x90)
[ 4115.217586] Exception stack(0xea8cff58 to 0xea8cffa0)
[ 4115.222613] ff40:
    00000000 0010afbc
[ 4115.230756] ff60: eb6c1330 c031ecc0 ea8ce000 c1d064ac c1d064f0
00000002 00000000 00000000
[ 4115.238900] ff80: c1c80ba8 ea8cffb0 00000000 ea8cffa8 c0309db4
c0309db8 600f0013 ffffffff
[ 4115.247049] [<c0301a8c>] (__irq_svc) from [<c0309db8>]
(arch_cpu_idle+0x38/0x3c)
[ 4115.254420] [<c0309db8>] (arch_cpu_idle) from [<c037aa04>]
(do_idle+0x1c8/0x2a4)
[ 4115.261789] [<c037aa04>] (do_idle) from [<c037ad7c>]
(cpu_startup_entry+0x18/0x1c)
[ 4115.269328] [<c037ad7c>] (cpu_startup_entry) from [<8030256c>] (0x8030256c)
[ 4115.276288] ---[ end trace c31a413a826ed6df ]---
[ 4115.291055] ------------[ cut here ]------------
[ 4115.295671] WARNING: CPU: 1 PID: 260 at
drivers/net/phy/phylink.c:1013 phylink_stop+0xd4/0xd8
[ 4115.304165] RTNL: assertion failed at drivers/net/phy/phylink.c (1013)
[ 4115.310651] Modules linked in:
[ 4115.313707] CPU: 1 PID: 260 Comm: kworker/1:4 Tainted: G        W
      5.3.0-rc6-01275-g246284a4920a-dirty #251
[ 4115.323996] Hardware name: Freescale LS1021A
[ 4115.328245] Workqueue: events gfar_reset_task
[ 4115.332589] [<c03132e0>] (unwind_backtrace) from [<c030d8b8>]
(show_stack+0x10/0x14)
[ 4115.340293] [<c030d8b8>] (show_stack) from [<c10b41f0>]
(dump_stack+0xb0/0xc4)
[ 4115.347480] [<c10b41f0>] (dump_stack) from [<c0349dc8>] (__warn+0xf8/0x110)
[ 4115.354406] [<c0349dc8>] (__warn) from [<c0349e24>]
(warn_slowpath_fmt+0x44/0x68)
[ 4115.361850] [<c0349e24>] (warn_slowpath_fmt) from [<c0b62918>]
(phylink_stop+0xd4/0xd8)
[ 4115.369813] [<c0b62918>] (phylink_stop) from [<c0bcc3c0>]
(stop_gfar+0x3c/0x48)
[ 4115.377085] [<c0bcc3c0>] (stop_gfar) from [<c0bccf68>] (reset_gfar+0x88/0xc8)
[ 4115.384185] [<c0bccf68>] (reset_gfar) from [<c03650d4>]
(process_one_work+0x218/0x510)
[ 4115.392062] [<c03650d4>] (process_one_work) from [<c0366544>]
(worker_thread+0x30/0x5a0)
[ 4115.400112] [<c0366544>] (worker_thread) from [<c036b33c>]
(kthread+0x120/0x150)
[ 4115.407469] [<c036b33c>] (kthread) from [<c03010e8>]
(ret_from_fork+0x14/0x2c)
[ 4115.414648] Exception stack(0xeaf8bfb0 to 0xeaf8bff8)
[ 4115.419668] bfa0:                                     00000000
00000000 00000000 00000000
[ 4115.427800] bfc0: 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000
[ 4115.435932] bfe0: 00000000 00000000 00000000 00000000 00000013 00000000
[ 4115.442529] ---[ end trace c31a413a826ed6e0 ]---
[ 4115.447454] fsl-gianfar soc:ethernet@2d90000 eth2: Link is Down
[ 4115.457917] ------------[ cut here ]------------
[ 4115.462605] WARNING: CPU: 1 PID: 260 at
drivers/net/phy/phylink.c:952 phylink_start+0x1b4/0x270
[ 4115.471257] RTNL: assertion failed at drivers/net/phy/phylink.c (952)
[ 4115.477682] Modules linked in:
[ 4115.480725] CPU: 1 PID: 260 Comm: kworker/1:4 Tainted: G        W
      5.3.0-rc6-01275-g246284a4920a-dirty #251
[ 4115.491014] Hardware name: Freescale LS1021A
[ 4115.495263] Workqueue: events gfar_reset_task
[ 4115.499609] [<c03132e0>] (unwind_backtrace) from [<c030d8b8>]
(show_stack+0x10/0x14)
[ 4115.507315] [<c030d8b8>] (show_stack) from [<c10b41f0>]
(dump_stack+0xb0/0xc4)
[ 4115.514502] [<c10b41f0>] (dump_stack) from [<c0349dc8>] (__warn+0xf8/0x110)
[ 4115.521428] [<c0349dc8>] (__warn) from [<c0349e24>]
(warn_slowpath_fmt+0x44/0x68)
[ 4115.528872] [<c0349e24>] (warn_slowpath_fmt) from [<c0b61c9c>]
(phylink_start+0x1b4/0x270)
[ 4115.537094] [<c0b61c9c>] (phylink_start) from [<c0bcc994>]
(startup_gfar+0x248/0x2fc)
[ 4115.544885] [<c0bcc994>] (startup_gfar) from [<c0bccf70>]
(reset_gfar+0x90/0xc8)
[ 4115.552244] [<c0bccf70>] (reset_gfar) from [<c03650d4>]
(process_one_work+0x218/0x510)
[ 4115.560121] [<c03650d4>] (process_one_work) from [<c0366544>]
(worker_thread+0x30/0x5a0)
[ 4115.568171] [<c0366544>] (worker_thread) from [<c036b33c>]
(kthread+0x120/0x150)
[ 4115.575528] [<c036b33c>] (kthread) from [<c03010e8>]
(ret_from_fork+0x14/0x2c)
[ 4115.582708] Exception stack(0xeaf8bfb0 to 0xeaf8bff8)
[ 4115.587730] bfa0:                                     00000000
00000000 00000000 00000000
[ 4115.595862] bfc0: 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000
[ 4115.603993] bfe0: 00000000 00000000 00000000 00000000 00000013 00000000
[ 4115.610607] ---[ end trace c31a413a826ed6e1 ]---

Hope this helps,
-Vladimir

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 0/4] gianfar: some assorted cleanup
  2019-08-24 15:21       ` Vladimir Oltean
  2019-08-28 15:20         ` Vladimir Oltean
@ 2019-09-04 13:52         ` Arseny Solokha
  2019-09-04 13:52           ` [PATCH 1/4] gianfar: remove forward declarations Arseny Solokha
                             ` (5 more replies)
  2019-09-04 13:54         ` [RFC PATCH 1/2] gianfar: convert to phylink Arseny Solokha
  2 siblings, 6 replies; 26+ messages in thread
From: Arseny Solokha @ 2019-09-04 13:52 UTC (permalink / raw)
  To: Claudiu Manoil, Ioana Ciornei, Russell King, Andrew Lunn,
	Vladimir Oltean, Florian Fainelli
  Cc: netdev, Arseny Solokha

This is a cleanup series for the gianfar Ethernet driver, following up a
discussion in [1]. It is intended to precede a conversion of gianfar from
PHYLIB to PHYLINK API, which will be submitted later in its version 2.
However, it won't make a conversion cleaner, except for the last patch in
this series. Obviously this series is not intended for -stable.

The first patch looks super controversial to me, as it moves lots of code
around for the sole purpose of getting rid of static forward declarations
in two translation units. On the other hand, this change is purely
mechanical and cannot do any harm other than cluttering git blame output.
I can prepare an alternative patch for only swapping adjacent functions
around, if necessary.

The second patch is a trivial follow-up to the first one, making functions
that are only called from the same translation unit static.

The third patch removes some now unused macro and structure definitions
from gianfar.h, slipped away from various cleanups in the past.

The fourth patch, also suggested in [1], makes the driver consistently use
PHY connection type value obtained from a Device Tree node, instead of
ignoring it and using the one auto-detected by MAC, when connecting to PHY.
Obviously a value has to be specified correctly in DT source, or omitted
altogether, in which case the driver will fall back to auto-detection. When
querying a DT node, the driver will also take both applicable properties
into account by making a proper API call instead of open-coding the lookup
half-way correctly.

[1] https://lore.kernel.org/netdev/CA+h21hruqt6nGG5ksDSwrGH_w5GtGF4fjAMCWJne7QJrjusERQ@mail.gmail.com/

Arseny Solokha (4):
  gianfar: remove forward declarations
  gianfar: make five functions static
  gianfar: cleanup gianfar.h
  gianfar: use DT more consistently when selecting PHY connection type

 drivers/net/ethernet/freescale/gianfar.c      | 4647 ++++++++---------
 drivers/net/ethernet/freescale/gianfar.h      |   45 -
 .../net/ethernet/freescale/gianfar_ethtool.c  |   13 -
 3 files changed, 2303 insertions(+), 2402 deletions(-)

-- 
2.23.0


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/4] gianfar: remove forward declarations
  2019-09-04 13:52         ` [PATCH 0/4] gianfar: some assorted cleanup Arseny Solokha
@ 2019-09-04 13:52           ` Arseny Solokha
  2019-09-04 13:52           ` [PATCH 2/4] gianfar: make five functions static Arseny Solokha
                             ` (4 subsequent siblings)
  5 siblings, 0 replies; 26+ messages in thread
From: Arseny Solokha @ 2019-09-04 13:52 UTC (permalink / raw)
  To: Claudiu Manoil, Ioana Ciornei, Russell King, Andrew Lunn,
	Vladimir Oltean, Florian Fainelli
  Cc: netdev, Arseny Solokha

Remove forward declarations of various static functions located in two
driver implementation files and rearrange the corresponding definitions
accordingly.

This patch only introduces mechanical changes, namely it removes forward
declarations and moves function definitions around; it does not change any
functionality.

Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru>
---
 drivers/net/ethernet/freescale/gianfar.c      | 2849 ++++++++---------
 drivers/net/ethernet/freescale/gianfar.h      |    7 -
 .../net/ethernet/freescale/gianfar_ethtool.c  |   13 -
 3 files changed, 1404 insertions(+), 1465 deletions(-)

diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 412c0340fed9..fc31ba1a8bb8 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -105,43 +105,6 @@
 
 const char gfar_driver_version[] = "2.0";
 
-static int gfar_enet_open(struct net_device *dev);
-static netdev_tx_t gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static void gfar_reset_task(struct work_struct *work);
-static void gfar_timeout(struct net_device *dev);
-static int gfar_close(struct net_device *dev);
-static void gfar_alloc_rx_buffs(struct gfar_priv_rx_q *rx_queue,
-				int alloc_cnt);
-static int gfar_set_mac_address(struct net_device *dev);
-static int gfar_change_mtu(struct net_device *dev, int new_mtu);
-static irqreturn_t gfar_error(int irq, void *dev_id);
-static irqreturn_t gfar_transmit(int irq, void *dev_id);
-static irqreturn_t gfar_interrupt(int irq, void *dev_id);
-static void adjust_link(struct net_device *dev);
-static noinline void gfar_update_link_state(struct gfar_private *priv);
-static int init_phy(struct net_device *dev);
-static int gfar_probe(struct platform_device *ofdev);
-static int gfar_remove(struct platform_device *ofdev);
-static void free_skb_resources(struct gfar_private *priv);
-static void gfar_set_multi(struct net_device *dev);
-static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
-static void gfar_configure_serdes(struct net_device *dev);
-static int gfar_poll_rx(struct napi_struct *napi, int budget);
-static int gfar_poll_tx(struct napi_struct *napi, int budget);
-static int gfar_poll_rx_sq(struct napi_struct *napi, int budget);
-static int gfar_poll_tx_sq(struct napi_struct *napi, int budget);
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void gfar_netpoll(struct net_device *dev);
-#endif
-int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit);
-static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue);
-static void gfar_process_frame(struct net_device *ndev, struct sk_buff *skb);
-static void gfar_halt_nodisable(struct gfar_private *priv);
-static void gfar_clear_exact_match(struct net_device *dev);
-static void gfar_set_mac_for_addr(struct net_device *dev, int num,
-				  const u8 *addr);
-static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-
 MODULE_AUTHOR("Freescale Semiconductor, Inc");
 MODULE_DESCRIPTION("Gianfar Ethernet Driver");
 MODULE_LICENSE("GPL");
@@ -162,138 +125,6 @@ static void gfar_init_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp,
 	bdp->lstatus = cpu_to_be32(lstatus);
 }
 
-static void gfar_init_bds(struct net_device *ndev)
-{
-	struct gfar_private *priv = netdev_priv(ndev);
-	struct gfar __iomem *regs = priv->gfargrp[0].regs;
-	struct gfar_priv_tx_q *tx_queue = NULL;
-	struct gfar_priv_rx_q *rx_queue = NULL;
-	struct txbd8 *txbdp;
-	u32 __iomem *rfbptr;
-	int i, j;
-
-	for (i = 0; i < priv->num_tx_queues; i++) {
-		tx_queue = priv->tx_queue[i];
-		/* Initialize some variables in our dev structure */
-		tx_queue->num_txbdfree = tx_queue->tx_ring_size;
-		tx_queue->dirty_tx = tx_queue->tx_bd_base;
-		tx_queue->cur_tx = tx_queue->tx_bd_base;
-		tx_queue->skb_curtx = 0;
-		tx_queue->skb_dirtytx = 0;
-
-		/* Initialize Transmit Descriptor Ring */
-		txbdp = tx_queue->tx_bd_base;
-		for (j = 0; j < tx_queue->tx_ring_size; j++) {
-			txbdp->lstatus = 0;
-			txbdp->bufPtr = 0;
-			txbdp++;
-		}
-
-		/* Set the last descriptor in the ring to indicate wrap */
-		txbdp--;
-		txbdp->status = cpu_to_be16(be16_to_cpu(txbdp->status) |
-					    TXBD_WRAP);
-	}
-
-	rfbptr = &regs->rfbptr0;
-	for (i = 0; i < priv->num_rx_queues; i++) {
-		rx_queue = priv->rx_queue[i];
-
-		rx_queue->next_to_clean = 0;
-		rx_queue->next_to_use = 0;
-		rx_queue->next_to_alloc = 0;
-
-		/* make sure next_to_clean != next_to_use after this
-		 * by leaving at least 1 unused descriptor
-		 */
-		gfar_alloc_rx_buffs(rx_queue, gfar_rxbd_unused(rx_queue));
-
-		rx_queue->rfbptr = rfbptr;
-		rfbptr += 2;
-	}
-}
-
-static int gfar_alloc_skb_resources(struct net_device *ndev)
-{
-	void *vaddr;
-	dma_addr_t addr;
-	int i, j;
-	struct gfar_private *priv = netdev_priv(ndev);
-	struct device *dev = priv->dev;
-	struct gfar_priv_tx_q *tx_queue = NULL;
-	struct gfar_priv_rx_q *rx_queue = NULL;
-
-	priv->total_tx_ring_size = 0;
-	for (i = 0; i < priv->num_tx_queues; i++)
-		priv->total_tx_ring_size += priv->tx_queue[i]->tx_ring_size;
-
-	priv->total_rx_ring_size = 0;
-	for (i = 0; i < priv->num_rx_queues; i++)
-		priv->total_rx_ring_size += priv->rx_queue[i]->rx_ring_size;
-
-	/* Allocate memory for the buffer descriptors */
-	vaddr = dma_alloc_coherent(dev,
-				   (priv->total_tx_ring_size *
-				    sizeof(struct txbd8)) +
-				   (priv->total_rx_ring_size *
-				    sizeof(struct rxbd8)),
-				   &addr, GFP_KERNEL);
-	if (!vaddr)
-		return -ENOMEM;
-
-	for (i = 0; i < priv->num_tx_queues; i++) {
-		tx_queue = priv->tx_queue[i];
-		tx_queue->tx_bd_base = vaddr;
-		tx_queue->tx_bd_dma_base = addr;
-		tx_queue->dev = ndev;
-		/* enet DMA only understands physical addresses */
-		addr  += sizeof(struct txbd8) * tx_queue->tx_ring_size;
-		vaddr += sizeof(struct txbd8) * tx_queue->tx_ring_size;
-	}
-
-	/* Start the rx descriptor ring where the tx ring leaves off */
-	for (i = 0; i < priv->num_rx_queues; i++) {
-		rx_queue = priv->rx_queue[i];
-		rx_queue->rx_bd_base = vaddr;
-		rx_queue->rx_bd_dma_base = addr;
-		rx_queue->ndev = ndev;
-		rx_queue->dev = dev;
-		addr  += sizeof(struct rxbd8) * rx_queue->rx_ring_size;
-		vaddr += sizeof(struct rxbd8) * rx_queue->rx_ring_size;
-	}
-
-	/* Setup the skbuff rings */
-	for (i = 0; i < priv->num_tx_queues; i++) {
-		tx_queue = priv->tx_queue[i];
-		tx_queue->tx_skbuff =
-			kmalloc_array(tx_queue->tx_ring_size,
-				      sizeof(*tx_queue->tx_skbuff),
-				      GFP_KERNEL);
-		if (!tx_queue->tx_skbuff)
-			goto cleanup;
-
-		for (j = 0; j < tx_queue->tx_ring_size; j++)
-			tx_queue->tx_skbuff[j] = NULL;
-	}
-
-	for (i = 0; i < priv->num_rx_queues; i++) {
-		rx_queue = priv->rx_queue[i];
-		rx_queue->rx_buff = kcalloc(rx_queue->rx_ring_size,
-					    sizeof(*rx_queue->rx_buff),
-					    GFP_KERNEL);
-		if (!rx_queue->rx_buff)
-			goto cleanup;
-	}
-
-	gfar_init_bds(ndev);
-
-	return 0;
-
-cleanup:
-	free_skb_resources(priv);
-	return -ENOMEM;
-}
-
 static void gfar_init_tx_rx_base(struct gfar_private *priv)
 {
 	struct gfar __iomem *regs = priv->gfargrp[0].regs;
@@ -477,6 +308,62 @@ static struct net_device_stats *gfar_get_stats(struct net_device *dev)
 	return &dev->stats;
 }
 
+/* Set the appropriate hash bit for the given addr */
+/* The algorithm works like so:
+ * 1) Take the Destination Address (ie the multicast address), and
+ * do a CRC on it (little endian), and reverse the bits of the
+ * result.
+ * 2) Use the 8 most significant bits as a hash into a 256-entry
+ * table.  The table is controlled through 8 32-bit registers:
+ * gaddr0-7.  gaddr0's MSB is entry 0, and gaddr7's LSB is
+ * gaddr7.  This means that the 3 most significant bits in the
+ * hash index which gaddr register to use, and the 5 other bits
+ * indicate which bit (assuming an IBM numbering scheme, which
+ * for PowerPC (tm) is usually the case) in the register holds
+ * the entry.
+ */
+static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr)
+{
+	u32 tempval;
+	struct gfar_private *priv = netdev_priv(dev);
+	u32 result = ether_crc(ETH_ALEN, addr);
+	int width = priv->hash_width;
+	u8 whichbit = (result >> (32 - width)) & 0x1f;
+	u8 whichreg = result >> (32 - width + 5);
+	u32 value = (1 << (31-whichbit));
+
+	tempval = gfar_read(priv->hash_regs[whichreg]);
+	tempval |= value;
+	gfar_write(priv->hash_regs[whichreg], tempval);
+}
+
+/* There are multiple MAC Address register pairs on some controllers
+ * This function sets the numth pair to a given address
+ */
+static void gfar_set_mac_for_addr(struct net_device *dev, int num,
+				  const u8 *addr)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+	struct gfar __iomem *regs = priv->gfargrp[0].regs;
+	u32 tempval;
+	u32 __iomem *macptr = &regs->macstnaddr1;
+
+	macptr += num*2;
+
+	/* For a station address of 0x12345678ABCD in transmission
+	 * order (BE), MACnADDR1 is set to 0xCDAB7856 and
+	 * MACnADDR2 is set to 0x34120000.
+	 */
+	tempval = (addr[5] << 24) | (addr[4] << 16) |
+		  (addr[3] << 8)  |  addr[2];
+
+	gfar_write(macptr, tempval);
+
+	tempval = (addr[1] << 24) | (addr[0] << 16);
+
+	gfar_write(macptr+1, tempval);
+}
+
 static int gfar_set_mac_addr(struct net_device *dev, void *p)
 {
 	eth_mac_addr(dev, p);
@@ -486,24 +373,6 @@ static int gfar_set_mac_addr(struct net_device *dev, void *p)
 	return 0;
 }
 
-static const struct net_device_ops gfar_netdev_ops = {
-	.ndo_open = gfar_enet_open,
-	.ndo_start_xmit = gfar_start_xmit,
-	.ndo_stop = gfar_close,
-	.ndo_change_mtu = gfar_change_mtu,
-	.ndo_set_features = gfar_set_features,
-	.ndo_set_rx_mode = gfar_set_multi,
-	.ndo_tx_timeout = gfar_timeout,
-	.ndo_do_ioctl = gfar_ioctl,
-	.ndo_get_stats = gfar_get_stats,
-	.ndo_change_carrier = fixed_phy_change_carrier,
-	.ndo_set_mac_address = gfar_set_mac_addr,
-	.ndo_validate_addr = eth_validate_addr,
-#ifdef CONFIG_NET_POLL_CONTROLLER
-	.ndo_poll_controller = gfar_netpoll,
-#endif
-};
-
 static void gfar_ints_disable(struct gfar_private *priv)
 {
 	int i;
@@ -723,6 +592,50 @@ static int gfar_of_group_count(struct device_node *np)
 	return num;
 }
 
+/* Reads the controller's registers to determine what interface
+ * connects it to the PHY.
+ */
+static phy_interface_t gfar_get_interface(struct net_device *dev)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+	struct gfar __iomem *regs = priv->gfargrp[0].regs;
+	u32 ecntrl;
+
+	ecntrl = gfar_read(&regs->ecntrl);
+
+	if (ecntrl & ECNTRL_SGMII_MODE)
+		return PHY_INTERFACE_MODE_SGMII;
+
+	if (ecntrl & ECNTRL_TBI_MODE) {
+		if (ecntrl & ECNTRL_REDUCED_MODE)
+			return PHY_INTERFACE_MODE_RTBI;
+		else
+			return PHY_INTERFACE_MODE_TBI;
+	}
+
+	if (ecntrl & ECNTRL_REDUCED_MODE) {
+		if (ecntrl & ECNTRL_REDUCED_MII_MODE) {
+			return PHY_INTERFACE_MODE_RMII;
+		}
+		else {
+			phy_interface_t interface = priv->interface;
+
+			/* This isn't autodetected right now, so it must
+			 * be set by the device tree or platform code.
+			 */
+			if (interface == PHY_INTERFACE_MODE_RGMII_ID)
+				return PHY_INTERFACE_MODE_RGMII_ID;
+
+			return PHY_INTERFACE_MODE_RGMII;
+		}
+	}
+
+	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
+		return PHY_INTERFACE_MODE_GMII;
+
+	return PHY_INTERFACE_MODE_MII;
+}
+
 static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
 {
 	const char *model;
@@ -931,85 +844,6 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
 	return err;
 }
 
-static int gfar_hwtstamp_set(struct net_device *netdev, struct ifreq *ifr)
-{
-	struct hwtstamp_config config;
-	struct gfar_private *priv = netdev_priv(netdev);
-
-	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
-		return -EFAULT;
-
-	/* reserved for future extensions */
-	if (config.flags)
-		return -EINVAL;
-
-	switch (config.tx_type) {
-	case HWTSTAMP_TX_OFF:
-		priv->hwts_tx_en = 0;
-		break;
-	case HWTSTAMP_TX_ON:
-		if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER))
-			return -ERANGE;
-		priv->hwts_tx_en = 1;
-		break;
-	default:
-		return -ERANGE;
-	}
-
-	switch (config.rx_filter) {
-	case HWTSTAMP_FILTER_NONE:
-		if (priv->hwts_rx_en) {
-			priv->hwts_rx_en = 0;
-			reset_gfar(netdev);
-		}
-		break;
-	default:
-		if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER))
-			return -ERANGE;
-		if (!priv->hwts_rx_en) {
-			priv->hwts_rx_en = 1;
-			reset_gfar(netdev);
-		}
-		config.rx_filter = HWTSTAMP_FILTER_ALL;
-		break;
-	}
-
-	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
-		-EFAULT : 0;
-}
-
-static int gfar_hwtstamp_get(struct net_device *netdev, struct ifreq *ifr)
-{
-	struct hwtstamp_config config;
-	struct gfar_private *priv = netdev_priv(netdev);
-
-	config.flags = 0;
-	config.tx_type = priv->hwts_tx_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
-	config.rx_filter = (priv->hwts_rx_en ?
-			    HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE);
-
-	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
-		-EFAULT : 0;
-}
-
-static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
-	struct phy_device *phydev = dev->phydev;
-
-	if (!netif_running(dev))
-		return -EINVAL;
-
-	if (cmd == SIOCSHWTSTAMP)
-		return gfar_hwtstamp_set(dev, rq);
-	if (cmd == SIOCGHWTSTAMP)
-		return gfar_hwtstamp_get(dev, rq);
-
-	if (!phydev)
-		return -ENODEV;
-
-	return phy_mii_ioctl(phydev, rq, cmd);
-}
-
 static u32 cluster_entry_per_class(struct gfar_private *priv, u32 rqfar,
 				   u32 class)
 {
@@ -1133,135 +967,6 @@ static void gfar_detect_errata(struct gfar_private *priv)
 			 priv->errata);
 }
 
-void gfar_mac_reset(struct gfar_private *priv)
-{
-	struct gfar __iomem *regs = priv->gfargrp[0].regs;
-	u32 tempval;
-
-	/* Reset MAC layer */
-	gfar_write(&regs->maccfg1, MACCFG1_SOFT_RESET);
-
-	/* We need to delay at least 3 TX clocks */
-	udelay(3);
-
-	/* the soft reset bit is not self-resetting, so we need to
-	 * clear it before resuming normal operation
-	 */
-	gfar_write(&regs->maccfg1, 0);
-
-	udelay(3);
-
-	gfar_rx_offload_en(priv);
-
-	/* Initialize the max receive frame/buffer lengths */
-	gfar_write(&regs->maxfrm, GFAR_JUMBO_FRAME_SIZE);
-	gfar_write(&regs->mrblr, GFAR_RXB_SIZE);
-
-	/* Initialize the Minimum Frame Length Register */
-	gfar_write(&regs->minflr, MINFLR_INIT_SETTINGS);
-
-	/* Initialize MACCFG2. */
-	tempval = MACCFG2_INIT_SETTINGS;
-
-	/* eTSEC74 erratum: Rx frames of length MAXFRM or MAXFRM-1
-	 * are marked as truncated.  Avoid this by MACCFG2[Huge Frame]=1,
-	 * and by checking RxBD[LG] and discarding larger than MAXFRM.
-	 */
-	if (gfar_has_errata(priv, GFAR_ERRATA_74))
-		tempval |= MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK;
-
-	gfar_write(&regs->maccfg2, tempval);
-
-	/* Clear mac addr hash registers */
-	gfar_write(&regs->igaddr0, 0);
-	gfar_write(&regs->igaddr1, 0);
-	gfar_write(&regs->igaddr2, 0);
-	gfar_write(&regs->igaddr3, 0);
-	gfar_write(&regs->igaddr4, 0);
-	gfar_write(&regs->igaddr5, 0);
-	gfar_write(&regs->igaddr6, 0);
-	gfar_write(&regs->igaddr7, 0);
-
-	gfar_write(&regs->gaddr0, 0);
-	gfar_write(&regs->gaddr1, 0);
-	gfar_write(&regs->gaddr2, 0);
-	gfar_write(&regs->gaddr3, 0);
-	gfar_write(&regs->gaddr4, 0);
-	gfar_write(&regs->gaddr5, 0);
-	gfar_write(&regs->gaddr6, 0);
-	gfar_write(&regs->gaddr7, 0);
-
-	if (priv->extended_hash)
-		gfar_clear_exact_match(priv->ndev);
-
-	gfar_mac_rx_config(priv);
-
-	gfar_mac_tx_config(priv);
-
-	gfar_set_mac_address(priv->ndev);
-
-	gfar_set_multi(priv->ndev);
-
-	/* clear ievent and imask before configuring coalescing */
-	gfar_ints_disable(priv);
-
-	/* Configure the coalescing support */
-	gfar_configure_coalescing_all(priv);
-}
-
-static void gfar_hw_init(struct gfar_private *priv)
-{
-	struct gfar __iomem *regs = priv->gfargrp[0].regs;
-	u32 attrs;
-
-	/* Stop the DMA engine now, in case it was running before
-	 * (The firmware could have used it, and left it running).
-	 */
-	gfar_halt(priv);
-
-	gfar_mac_reset(priv);
-
-	/* Zero out the rmon mib registers if it has them */
-	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
-		memset_io(&(regs->rmon), 0, sizeof(struct rmon_mib));
-
-		/* Mask off the CAM interrupts */
-		gfar_write(&regs->rmon.cam1, 0xffffffff);
-		gfar_write(&regs->rmon.cam2, 0xffffffff);
-	}
-
-	/* Initialize ECNTRL */
-	gfar_write(&regs->ecntrl, ECNTRL_INIT_SETTINGS);
-
-	/* Set the extraction length and index */
-	attrs = ATTRELI_EL(priv->rx_stash_size) |
-		ATTRELI_EI(priv->rx_stash_index);
-
-	gfar_write(&regs->attreli, attrs);
-
-	/* Start with defaults, and add stashing
-	 * depending on driver parameters
-	 */
-	attrs = ATTR_INIT_SETTINGS;
-
-	if (priv->bd_stash_en)
-		attrs |= ATTR_BDSTASH;
-
-	if (priv->rx_stash_size != 0)
-		attrs |= ATTR_BUFSTASH;
-
-	gfar_write(&regs->attr, attrs);
-
-	/* FIFO configs */
-	gfar_write(&regs->fifo_tx_thr, DEFAULT_FIFO_TX_THR);
-	gfar_write(&regs->fifo_tx_starve, DEFAULT_FIFO_TX_STARVE);
-	gfar_write(&regs->fifo_tx_starve_shutoff, DEFAULT_FIFO_TX_STARVE_OFF);
-
-	/* Program the interrupt steering regs, only for MG devices */
-	if (priv->num_grps > 1)
-		gfar_write_isrg(priv);
-}
-
 static void gfar_init_addr_hash_table(struct gfar_private *priv)
 {
 	struct gfar __iomem *regs = priv->gfargrp[0].regs;
@@ -1302,578 +1007,6 @@ static void gfar_init_addr_hash_table(struct gfar_private *priv)
 	}
 }
 
-/* Set up the ethernet device structure, private data,
- * and anything else we need before we start
- */
-static int gfar_probe(struct platform_device *ofdev)
-{
-	struct device_node *np = ofdev->dev.of_node;
-	struct net_device *dev = NULL;
-	struct gfar_private *priv = NULL;
-	int err = 0, i;
-
-	err = gfar_of_init(ofdev, &dev);
-
-	if (err)
-		return err;
-
-	priv = netdev_priv(dev);
-	priv->ndev = dev;
-	priv->ofdev = ofdev;
-	priv->dev = &ofdev->dev;
-	SET_NETDEV_DEV(dev, &ofdev->dev);
-
-	INIT_WORK(&priv->reset_task, gfar_reset_task);
-
-	platform_set_drvdata(ofdev, priv);
-
-	gfar_detect_errata(priv);
-
-	/* Set the dev->base_addr to the gfar reg region */
-	dev->base_addr = (unsigned long) priv->gfargrp[0].regs;
-
-	/* Fill in the dev structure */
-	dev->watchdog_timeo = TX_TIMEOUT;
-	/* MTU range: 50 - 9586 */
-	dev->mtu = 1500;
-	dev->min_mtu = 50;
-	dev->max_mtu = GFAR_JUMBO_FRAME_SIZE - ETH_HLEN;
-	dev->netdev_ops = &gfar_netdev_ops;
-	dev->ethtool_ops = &gfar_ethtool_ops;
-
-	/* Register for napi ...We are registering NAPI for each grp */
-	for (i = 0; i < priv->num_grps; i++) {
-		if (priv->poll_mode == GFAR_SQ_POLLING) {
-			netif_napi_add(dev, &priv->gfargrp[i].napi_rx,
-				       gfar_poll_rx_sq, GFAR_DEV_WEIGHT);
-			netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx,
-				       gfar_poll_tx_sq, 2);
-		} else {
-			netif_napi_add(dev, &priv->gfargrp[i].napi_rx,
-				       gfar_poll_rx, GFAR_DEV_WEIGHT);
-			netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx,
-				       gfar_poll_tx, 2);
-		}
-	}
-
-	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
-		dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
-				   NETIF_F_RXCSUM;
-		dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG |
-				 NETIF_F_RXCSUM | NETIF_F_HIGHDMA;
-	}
-
-	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
-		dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX |
-				    NETIF_F_HW_VLAN_CTAG_RX;
-		dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
-	}
-
-	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
-
-	gfar_init_addr_hash_table(priv);
-
-	/* Insert receive time stamps into padding alignment bytes, and
-	 * plus 2 bytes padding to ensure the cpu alignment.
-	 */
-	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)
-		priv->padding = 8 + DEFAULT_PADDING;
-
-	if (dev->features & NETIF_F_IP_CSUM ||
-	    priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)
-		dev->needed_headroom = GMAC_FCB_LEN;
-
-	/* Initializing some of the rx/tx queue level parameters */
-	for (i = 0; i < priv->num_tx_queues; i++) {
-		priv->tx_queue[i]->tx_ring_size = DEFAULT_TX_RING_SIZE;
-		priv->tx_queue[i]->num_txbdfree = DEFAULT_TX_RING_SIZE;
-		priv->tx_queue[i]->txcoalescing = DEFAULT_TX_COALESCE;
-		priv->tx_queue[i]->txic = DEFAULT_TXIC;
-	}
-
-	for (i = 0; i < priv->num_rx_queues; i++) {
-		priv->rx_queue[i]->rx_ring_size = DEFAULT_RX_RING_SIZE;
-		priv->rx_queue[i]->rxcoalescing = DEFAULT_RX_COALESCE;
-		priv->rx_queue[i]->rxic = DEFAULT_RXIC;
-	}
-
-	/* Always enable rx filer if available */
-	priv->rx_filer_enable =
-	    (priv->device_flags & FSL_GIANFAR_DEV_HAS_RX_FILER) ? 1 : 0;
-	/* Enable most messages by default */
-	priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
-	/* use pritority h/w tx queue scheduling for single queue devices */
-	if (priv->num_tx_queues == 1)
-		priv->prio_sched_en = 1;
-
-	set_bit(GFAR_DOWN, &priv->state);
-
-	gfar_hw_init(priv);
-
-	/* Carrier starts down, phylib will bring it up */
-	netif_carrier_off(dev);
-
-	err = register_netdev(dev);
-
-	if (err) {
-		pr_err("%s: Cannot register net device, aborting\n", dev->name);
-		goto register_fail;
-	}
-
-	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET)
-		priv->wol_supported |= GFAR_WOL_MAGIC;
-
-	if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER) &&
-	    priv->rx_filer_enable)
-		priv->wol_supported |= GFAR_WOL_FILER_UCAST;
-
-	device_set_wakeup_capable(&ofdev->dev, priv->wol_supported);
-
-	/* fill out IRQ number and name fields */
-	for (i = 0; i < priv->num_grps; i++) {
-		struct gfar_priv_grp *grp = &priv->gfargrp[i];
-		if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
-			sprintf(gfar_irq(grp, TX)->name, "%s%s%c%s",
-				dev->name, "_g", '0' + i, "_tx");
-			sprintf(gfar_irq(grp, RX)->name, "%s%s%c%s",
-				dev->name, "_g", '0' + i, "_rx");
-			sprintf(gfar_irq(grp, ER)->name, "%s%s%c%s",
-				dev->name, "_g", '0' + i, "_er");
-		} else
-			strcpy(gfar_irq(grp, TX)->name, dev->name);
-	}
-
-	/* Initialize the filer table */
-	gfar_init_filer_table(priv);
-
-	/* Print out the device info */
-	netdev_info(dev, "mac: %pM\n", dev->dev_addr);
-
-	/* Even more device info helps when determining which kernel
-	 * provided which set of benchmarks.
-	 */
-	netdev_info(dev, "Running with NAPI enabled\n");
-	for (i = 0; i < priv->num_rx_queues; i++)
-		netdev_info(dev, "RX BD ring size for Q[%d]: %d\n",
-			    i, priv->rx_queue[i]->rx_ring_size);
-	for (i = 0; i < priv->num_tx_queues; i++)
-		netdev_info(dev, "TX BD ring size for Q[%d]: %d\n",
-			    i, priv->tx_queue[i]->tx_ring_size);
-
-	return 0;
-
-register_fail:
-	if (of_phy_is_fixed_link(np))
-		of_phy_deregister_fixed_link(np);
-	unmap_group_regs(priv);
-	gfar_free_rx_queues(priv);
-	gfar_free_tx_queues(priv);
-	of_node_put(priv->phy_node);
-	of_node_put(priv->tbi_node);
-	free_gfar_dev(priv);
-	return err;
-}
-
-static int gfar_remove(struct platform_device *ofdev)
-{
-	struct gfar_private *priv = platform_get_drvdata(ofdev);
-	struct device_node *np = ofdev->dev.of_node;
-
-	of_node_put(priv->phy_node);
-	of_node_put(priv->tbi_node);
-
-	unregister_netdev(priv->ndev);
-
-	if (of_phy_is_fixed_link(np))
-		of_phy_deregister_fixed_link(np);
-
-	unmap_group_regs(priv);
-	gfar_free_rx_queues(priv);
-	gfar_free_tx_queues(priv);
-	free_gfar_dev(priv);
-
-	return 0;
-}
-
-#ifdef CONFIG_PM
-
-static void __gfar_filer_disable(struct gfar_private *priv)
-{
-	struct gfar __iomem *regs = priv->gfargrp[0].regs;
-	u32 temp;
-
-	temp = gfar_read(&regs->rctrl);
-	temp &= ~(RCTRL_FILREN | RCTRL_PRSDEP_INIT);
-	gfar_write(&regs->rctrl, temp);
-}
-
-static void __gfar_filer_enable(struct gfar_private *priv)
-{
-	struct gfar __iomem *regs = priv->gfargrp[0].regs;
-	u32 temp;
-
-	temp = gfar_read(&regs->rctrl);
-	temp |= RCTRL_FILREN | RCTRL_PRSDEP_INIT;
-	gfar_write(&regs->rctrl, temp);
-}
-
-/* Filer rules implementing wol capabilities */
-static void gfar_filer_config_wol(struct gfar_private *priv)
-{
-	unsigned int i;
-	u32 rqfcr;
-
-	__gfar_filer_disable(priv);
-
-	/* clear the filer table, reject any packet by default */
-	rqfcr = RQFCR_RJE | RQFCR_CMP_MATCH;
-	for (i = 0; i <= MAX_FILER_IDX; i++)
-		gfar_write_filer(priv, i, rqfcr, 0);
-
-	i = 0;
-	if (priv->wol_opts & GFAR_WOL_FILER_UCAST) {
-		/* unicast packet, accept it */
-		struct net_device *ndev = priv->ndev;
-		/* get the default rx queue index */
-		u8 qindex = (u8)priv->gfargrp[0].rx_queue->qindex;
-		u32 dest_mac_addr = (ndev->dev_addr[0] << 16) |
-				    (ndev->dev_addr[1] << 8) |
-				     ndev->dev_addr[2];
-
-		rqfcr = (qindex << 10) | RQFCR_AND |
-			RQFCR_CMP_EXACT | RQFCR_PID_DAH;
-
-		gfar_write_filer(priv, i++, rqfcr, dest_mac_addr);
-
-		dest_mac_addr = (ndev->dev_addr[3] << 16) |
-				(ndev->dev_addr[4] << 8) |
-				 ndev->dev_addr[5];
-		rqfcr = (qindex << 10) | RQFCR_GPI |
-			RQFCR_CMP_EXACT | RQFCR_PID_DAL;
-		gfar_write_filer(priv, i++, rqfcr, dest_mac_addr);
-	}
-
-	__gfar_filer_enable(priv);
-}
-
-static void gfar_filer_restore_table(struct gfar_private *priv)
-{
-	u32 rqfcr, rqfpr;
-	unsigned int i;
-
-	__gfar_filer_disable(priv);
-
-	for (i = 0; i <= MAX_FILER_IDX; i++) {
-		rqfcr = priv->ftp_rqfcr[i];
-		rqfpr = priv->ftp_rqfpr[i];
-		gfar_write_filer(priv, i, rqfcr, rqfpr);
-	}
-
-	__gfar_filer_enable(priv);
-}
-
-/* gfar_start() for Rx only and with the FGPI filer interrupt enabled */
-static void gfar_start_wol_filer(struct gfar_private *priv)
-{
-	struct gfar __iomem *regs = priv->gfargrp[0].regs;
-	u32 tempval;
-	int i = 0;
-
-	/* Enable Rx hw queues */
-	gfar_write(&regs->rqueue, priv->rqueue);
-
-	/* Initialize DMACTRL to have WWR and WOP */
-	tempval = gfar_read(&regs->dmactrl);
-	tempval |= DMACTRL_INIT_SETTINGS;
-	gfar_write(&regs->dmactrl, tempval);
-
-	/* Make sure we aren't stopped */
-	tempval = gfar_read(&regs->dmactrl);
-	tempval &= ~DMACTRL_GRS;
-	gfar_write(&regs->dmactrl, tempval);
-
-	for (i = 0; i < priv->num_grps; i++) {
-		regs = priv->gfargrp[i].regs;
-		/* Clear RHLT, so that the DMA starts polling now */
-		gfar_write(&regs->rstat, priv->gfargrp[i].rstat);
-		/* enable the Filer General Purpose Interrupt */
-		gfar_write(&regs->imask, IMASK_FGPI);
-	}
-
-	/* Enable Rx DMA */
-	tempval = gfar_read(&regs->maccfg1);
-	tempval |= MACCFG1_RX_EN;
-	gfar_write(&regs->maccfg1, tempval);
-}
-
-static int gfar_suspend(struct device *dev)
-{
-	struct gfar_private *priv = dev_get_drvdata(dev);
-	struct net_device *ndev = priv->ndev;
-	struct gfar __iomem *regs = priv->gfargrp[0].regs;
-	u32 tempval;
-	u16 wol = priv->wol_opts;
-
-	if (!netif_running(ndev))
-		return 0;
-
-	disable_napi(priv);
-	netif_tx_lock(ndev);
-	netif_device_detach(ndev);
-	netif_tx_unlock(ndev);
-
-	gfar_halt(priv);
-
-	if (wol & GFAR_WOL_MAGIC) {
-		/* Enable interrupt on Magic Packet */
-		gfar_write(&regs->imask, IMASK_MAG);
-
-		/* Enable Magic Packet mode */
-		tempval = gfar_read(&regs->maccfg2);
-		tempval |= MACCFG2_MPEN;
-		gfar_write(&regs->maccfg2, tempval);
-
-		/* re-enable the Rx block */
-		tempval = gfar_read(&regs->maccfg1);
-		tempval |= MACCFG1_RX_EN;
-		gfar_write(&regs->maccfg1, tempval);
-
-	} else if (wol & GFAR_WOL_FILER_UCAST) {
-		gfar_filer_config_wol(priv);
-		gfar_start_wol_filer(priv);
-
-	} else {
-		phy_stop(ndev->phydev);
-	}
-
-	return 0;
-}
-
-static int gfar_resume(struct device *dev)
-{
-	struct gfar_private *priv = dev_get_drvdata(dev);
-	struct net_device *ndev = priv->ndev;
-	struct gfar __iomem *regs = priv->gfargrp[0].regs;
-	u32 tempval;
-	u16 wol = priv->wol_opts;
-
-	if (!netif_running(ndev))
-		return 0;
-
-	if (wol & GFAR_WOL_MAGIC) {
-		/* Disable Magic Packet mode */
-		tempval = gfar_read(&regs->maccfg2);
-		tempval &= ~MACCFG2_MPEN;
-		gfar_write(&regs->maccfg2, tempval);
-
-	} else if (wol & GFAR_WOL_FILER_UCAST) {
-		/* need to stop rx only, tx is already down */
-		gfar_halt(priv);
-		gfar_filer_restore_table(priv);
-
-	} else {
-		phy_start(ndev->phydev);
-	}
-
-	gfar_start(priv);
-
-	netif_device_attach(ndev);
-	enable_napi(priv);
-
-	return 0;
-}
-
-static int gfar_restore(struct device *dev)
-{
-	struct gfar_private *priv = dev_get_drvdata(dev);
-	struct net_device *ndev = priv->ndev;
-
-	if (!netif_running(ndev)) {
-		netif_device_attach(ndev);
-
-		return 0;
-	}
-
-	gfar_init_bds(ndev);
-
-	gfar_mac_reset(priv);
-
-	gfar_init_tx_rx_base(priv);
-
-	gfar_start(priv);
-
-	priv->oldlink = 0;
-	priv->oldspeed = 0;
-	priv->oldduplex = -1;
-
-	if (ndev->phydev)
-		phy_start(ndev->phydev);
-
-	netif_device_attach(ndev);
-	enable_napi(priv);
-
-	return 0;
-}
-
-static const struct dev_pm_ops gfar_pm_ops = {
-	.suspend = gfar_suspend,
-	.resume = gfar_resume,
-	.freeze = gfar_suspend,
-	.thaw = gfar_resume,
-	.restore = gfar_restore,
-};
-
-#define GFAR_PM_OPS (&gfar_pm_ops)
-
-#else
-
-#define GFAR_PM_OPS NULL
-
-#endif
-
-/* Reads the controller's registers to determine what interface
- * connects it to the PHY.
- */
-static phy_interface_t gfar_get_interface(struct net_device *dev)
-{
-	struct gfar_private *priv = netdev_priv(dev);
-	struct gfar __iomem *regs = priv->gfargrp[0].regs;
-	u32 ecntrl;
-
-	ecntrl = gfar_read(&regs->ecntrl);
-
-	if (ecntrl & ECNTRL_SGMII_MODE)
-		return PHY_INTERFACE_MODE_SGMII;
-
-	if (ecntrl & ECNTRL_TBI_MODE) {
-		if (ecntrl & ECNTRL_REDUCED_MODE)
-			return PHY_INTERFACE_MODE_RTBI;
-		else
-			return PHY_INTERFACE_MODE_TBI;
-	}
-
-	if (ecntrl & ECNTRL_REDUCED_MODE) {
-		if (ecntrl & ECNTRL_REDUCED_MII_MODE) {
-			return PHY_INTERFACE_MODE_RMII;
-		}
-		else {
-			phy_interface_t interface = priv->interface;
-
-			/* This isn't autodetected right now, so it must
-			 * be set by the device tree or platform code.
-			 */
-			if (interface == PHY_INTERFACE_MODE_RGMII_ID)
-				return PHY_INTERFACE_MODE_RGMII_ID;
-
-			return PHY_INTERFACE_MODE_RGMII;
-		}
-	}
-
-	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
-		return PHY_INTERFACE_MODE_GMII;
-
-	return PHY_INTERFACE_MODE_MII;
-}
-
-
-/* Initializes driver's PHY state, and attaches to the PHY.
- * Returns 0 on success.
- */
-static int init_phy(struct net_device *dev)
-{
-	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
-	struct gfar_private *priv = netdev_priv(dev);
-	phy_interface_t interface;
-	struct phy_device *phydev;
-	struct ethtool_eee edata;
-
-	linkmode_set_bit_array(phy_10_100_features_array,
-			       ARRAY_SIZE(phy_10_100_features_array),
-			       mask);
-	linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask);
-	linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, mask);
-	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
-		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mask);
-
-	priv->oldlink = 0;
-	priv->oldspeed = 0;
-	priv->oldduplex = -1;
-
-	interface = gfar_get_interface(dev);
-
-	phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0,
-				interface);
-	if (!phydev) {
-		dev_err(&dev->dev, "could not attach to PHY\n");
-		return -ENODEV;
-	}
-
-	if (interface == PHY_INTERFACE_MODE_SGMII)
-		gfar_configure_serdes(dev);
-
-	/* Remove any features not supported by the controller */
-	linkmode_and(phydev->supported, phydev->supported, mask);
-	linkmode_copy(phydev->advertising, phydev->supported);
-
-	/* Add support for flow control */
-	phy_support_asym_pause(phydev);
-
-	/* disable EEE autoneg, EEE not supported by eTSEC */
-	memset(&edata, 0, sizeof(struct ethtool_eee));
-	phy_ethtool_set_eee(phydev, &edata);
-
-	return 0;
-}
-
-/* Initialize TBI PHY interface for communicating with the
- * SERDES lynx PHY on the chip.  We communicate with this PHY
- * through the MDIO bus on each controller, treating it as a
- * "normal" PHY at the address found in the TBIPA register.  We assume
- * that the TBIPA register is valid.  Either the MDIO bus code will set
- * it to a value that doesn't conflict with other PHYs on the bus, or the
- * value doesn't matter, as there are no other PHYs on the bus.
- */
-static void gfar_configure_serdes(struct net_device *dev)
-{
-	struct gfar_private *priv = netdev_priv(dev);
-	struct phy_device *tbiphy;
-
-	if (!priv->tbi_node) {
-		dev_warn(&dev->dev, "error: SGMII mode requires that the "
-				    "device tree specify a tbi-handle\n");
-		return;
-	}
-
-	tbiphy = of_phy_find_device(priv->tbi_node);
-	if (!tbiphy) {
-		dev_err(&dev->dev, "error: Could not get TBI device\n");
-		return;
-	}
-
-	/* If the link is already up, we must already be ok, and don't need to
-	 * configure and reset the TBI<->SerDes link.  Maybe U-Boot configured
-	 * everything for us?  Resetting it takes the link down and requires
-	 * several seconds for it to come back.
-	 */
-	if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) {
-		put_device(&tbiphy->mdio.dev);
-		return;
-	}
-
-	/* Single clk mode, mii mode off(for serdes communication) */
-	phy_write(tbiphy, MII_TBICON, TBICON_CLK_SELECT);
-
-	phy_write(tbiphy, MII_ADVERTISE,
-		  ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE |
-		  ADVERTISE_1000XPSE_ASYM);
-
-	phy_write(tbiphy, MII_BMCR,
-		  BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX |
-		  BMCR_SPEED1000);
-
-	put_device(&tbiphy->mdio.dev);
-}
-
 static int __gfar_is_rx_idle(struct gfar_private *priv)
 {
 	u32 res;
@@ -1949,26 +1082,6 @@ void gfar_halt(struct gfar_private *priv)
 	gfar_write(&regs->maccfg1, tempval);
 }
 
-void stop_gfar(struct net_device *dev)
-{
-	struct gfar_private *priv = netdev_priv(dev);
-
-	netif_tx_stop_all_queues(dev);
-
-	smp_mb__before_atomic();
-	set_bit(GFAR_DOWN, &priv->state);
-	smp_mb__after_atomic();
-
-	disable_napi(priv);
-
-	/* disable ints and gracefully shut down Rx/Tx DMA */
-	gfar_halt(priv);
-
-	phy_stop(dev->phydev);
-
-	free_skb_resources(priv);
-}
-
 static void free_skb_tx_queue(struct gfar_priv_tx_q *tx_queue)
 {
 	struct txbd8 *txbdp;
@@ -2061,6 +1174,26 @@ static void free_skb_resources(struct gfar_private *priv)
 			  priv->tx_queue[0]->tx_bd_dma_base);
 }
 
+void stop_gfar(struct net_device *dev)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+
+	netif_tx_stop_all_queues(dev);
+
+	smp_mb__before_atomic();
+	set_bit(GFAR_DOWN, &priv->state);
+	smp_mb__after_atomic();
+
+	disable_napi(priv);
+
+	/* disable ints and gracefully shut down Rx/Tx DMA */
+	gfar_halt(priv);
+
+	phy_stop(dev->phydev);
+
+	free_skb_resources(priv);
+}
+
 void gfar_start(struct gfar_private *priv)
 {
 	struct gfar __iomem *regs = priv->gfargrp[0].regs;
@@ -2098,103 +1231,207 @@ void gfar_start(struct gfar_private *priv)
 	netif_trans_update(priv->ndev); /* prevent tx timeout */
 }
 
-static void free_grp_irqs(struct gfar_priv_grp *grp)
+static bool gfar_new_page(struct gfar_priv_rx_q *rxq, struct gfar_rx_buff *rxb)
 {
-	free_irq(gfar_irq(grp, TX)->irq, grp);
-	free_irq(gfar_irq(grp, RX)->irq, grp);
-	free_irq(gfar_irq(grp, ER)->irq, grp);
-}
+	struct page *page;
+	dma_addr_t addr;
 
-static int register_grp_irqs(struct gfar_priv_grp *grp)
-{
-	struct gfar_private *priv = grp->priv;
-	struct net_device *dev = priv->ndev;
-	int err;
+	page = dev_alloc_page();
+	if (unlikely(!page))
+		return false;
 
-	/* If the device has multiple interrupts, register for
-	 * them.  Otherwise, only register for the one
-	 */
-	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
-		/* Install our interrupt handlers for Error,
-		 * Transmit, and Receive
-		 */
-		err = request_irq(gfar_irq(grp, ER)->irq, gfar_error, 0,
-				  gfar_irq(grp, ER)->name, grp);
-		if (err < 0) {
-			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
-				  gfar_irq(grp, ER)->irq);
+	addr = dma_map_page(rxq->dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
+	if (unlikely(dma_mapping_error(rxq->dev, addr))) {
+		__free_page(page);
 
-			goto err_irq_fail;
-		}
-		enable_irq_wake(gfar_irq(grp, ER)->irq);
-
-		err = request_irq(gfar_irq(grp, TX)->irq, gfar_transmit, 0,
-				  gfar_irq(grp, TX)->name, grp);
-		if (err < 0) {
-			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
-				  gfar_irq(grp, TX)->irq);
-			goto tx_irq_fail;
-		}
-		err = request_irq(gfar_irq(grp, RX)->irq, gfar_receive, 0,
-				  gfar_irq(grp, RX)->name, grp);
-		if (err < 0) {
-			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
-				  gfar_irq(grp, RX)->irq);
-			goto rx_irq_fail;
-		}
-		enable_irq_wake(gfar_irq(grp, RX)->irq);
-
-	} else {
-		err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt, 0,
-				  gfar_irq(grp, TX)->name, grp);
-		if (err < 0) {
-			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
-				  gfar_irq(grp, TX)->irq);
-			goto err_irq_fail;
-		}
-		enable_irq_wake(gfar_irq(grp, TX)->irq);
+		return false;
 	}
 
-	return 0;
-
-rx_irq_fail:
-	free_irq(gfar_irq(grp, TX)->irq, grp);
-tx_irq_fail:
-	free_irq(gfar_irq(grp, ER)->irq, grp);
-err_irq_fail:
-	return err;
+	rxb->dma = addr;
+	rxb->page = page;
+	rxb->page_offset = 0;
 
+	return true;
 }
 
-static void gfar_free_irq(struct gfar_private *priv)
+static void gfar_rx_alloc_err(struct gfar_priv_rx_q *rx_queue)
 {
+	struct gfar_private *priv = netdev_priv(rx_queue->ndev);
+	struct gfar_extra_stats *estats = &priv->extra_stats;
+
+	netdev_err(rx_queue->ndev, "Can't alloc RX buffers\n");
+	atomic64_inc(&estats->rx_alloc_err);
+}
+
+static void gfar_alloc_rx_buffs(struct gfar_priv_rx_q *rx_queue,
+				int alloc_cnt)
+{
+	struct rxbd8 *bdp;
+	struct gfar_rx_buff *rxb;
 	int i;
 
-	/* Free the IRQs */
-	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
-		for (i = 0; i < priv->num_grps; i++)
-			free_grp_irqs(&priv->gfargrp[i]);
-	} else {
-		for (i = 0; i < priv->num_grps; i++)
-			free_irq(gfar_irq(&priv->gfargrp[i], TX)->irq,
-				 &priv->gfargrp[i]);
-	}
-}
+	i = rx_queue->next_to_use;
+	bdp = &rx_queue->rx_bd_base[i];
+	rxb = &rx_queue->rx_buff[i];
 
-static int gfar_request_irq(struct gfar_private *priv)
-{
-	int err, i, j;
+	while (alloc_cnt--) {
+		/* try reuse page */
+		if (unlikely(!rxb->page)) {
+			if (unlikely(!gfar_new_page(rx_queue, rxb))) {
+				gfar_rx_alloc_err(rx_queue);
+				break;
+			}
+		}
 
-	for (i = 0; i < priv->num_grps; i++) {
-		err = register_grp_irqs(&priv->gfargrp[i]);
-		if (err) {
-			for (j = 0; j < i; j++)
-				free_grp_irqs(&priv->gfargrp[j]);
-			return err;
+		/* Setup the new RxBD */
+		gfar_init_rxbdp(rx_queue, bdp,
+				rxb->dma + rxb->page_offset + RXBUF_ALIGNMENT);
+
+		/* Update to the next pointer */
+		bdp++;
+		rxb++;
+
+		if (unlikely(++i == rx_queue->rx_ring_size)) {
+			i = 0;
+			bdp = rx_queue->rx_bd_base;
+			rxb = rx_queue->rx_buff;
 		}
 	}
 
+	rx_queue->next_to_use = i;
+	rx_queue->next_to_alloc = i;
+}
+
+static void gfar_init_bds(struct net_device *ndev)
+{
+	struct gfar_private *priv = netdev_priv(ndev);
+	struct gfar __iomem *regs = priv->gfargrp[0].regs;
+	struct gfar_priv_tx_q *tx_queue = NULL;
+	struct gfar_priv_rx_q *rx_queue = NULL;
+	struct txbd8 *txbdp;
+	u32 __iomem *rfbptr;
+	int i, j;
+
+	for (i = 0; i < priv->num_tx_queues; i++) {
+		tx_queue = priv->tx_queue[i];
+		/* Initialize some variables in our dev structure */
+		tx_queue->num_txbdfree = tx_queue->tx_ring_size;
+		tx_queue->dirty_tx = tx_queue->tx_bd_base;
+		tx_queue->cur_tx = tx_queue->tx_bd_base;
+		tx_queue->skb_curtx = 0;
+		tx_queue->skb_dirtytx = 0;
+
+		/* Initialize Transmit Descriptor Ring */
+		txbdp = tx_queue->tx_bd_base;
+		for (j = 0; j < tx_queue->tx_ring_size; j++) {
+			txbdp->lstatus = 0;
+			txbdp->bufPtr = 0;
+			txbdp++;
+		}
+
+		/* Set the last descriptor in the ring to indicate wrap */
+		txbdp--;
+		txbdp->status = cpu_to_be16(be16_to_cpu(txbdp->status) |
+					    TXBD_WRAP);
+	}
+
+	rfbptr = &regs->rfbptr0;
+	for (i = 0; i < priv->num_rx_queues; i++) {
+		rx_queue = priv->rx_queue[i];
+
+		rx_queue->next_to_clean = 0;
+		rx_queue->next_to_use = 0;
+		rx_queue->next_to_alloc = 0;
+
+		/* make sure next_to_clean != next_to_use after this
+		 * by leaving at least 1 unused descriptor
+		 */
+		gfar_alloc_rx_buffs(rx_queue, gfar_rxbd_unused(rx_queue));
+
+		rx_queue->rfbptr = rfbptr;
+		rfbptr += 2;
+	}
+}
+
+static int gfar_alloc_skb_resources(struct net_device *ndev)
+{
+	void *vaddr;
+	dma_addr_t addr;
+	int i, j;
+	struct gfar_private *priv = netdev_priv(ndev);
+	struct device *dev = priv->dev;
+	struct gfar_priv_tx_q *tx_queue = NULL;
+	struct gfar_priv_rx_q *rx_queue = NULL;
+
+	priv->total_tx_ring_size = 0;
+	for (i = 0; i < priv->num_tx_queues; i++)
+		priv->total_tx_ring_size += priv->tx_queue[i]->tx_ring_size;
+
+	priv->total_rx_ring_size = 0;
+	for (i = 0; i < priv->num_rx_queues; i++)
+		priv->total_rx_ring_size += priv->rx_queue[i]->rx_ring_size;
+
+	/* Allocate memory for the buffer descriptors */
+	vaddr = dma_alloc_coherent(dev,
+				   (priv->total_tx_ring_size *
+				    sizeof(struct txbd8)) +
+				   (priv->total_rx_ring_size *
+				    sizeof(struct rxbd8)),
+				   &addr, GFP_KERNEL);
+	if (!vaddr)
+		return -ENOMEM;
+
+	for (i = 0; i < priv->num_tx_queues; i++) {
+		tx_queue = priv->tx_queue[i];
+		tx_queue->tx_bd_base = vaddr;
+		tx_queue->tx_bd_dma_base = addr;
+		tx_queue->dev = ndev;
+		/* enet DMA only understands physical addresses */
+		addr  += sizeof(struct txbd8) * tx_queue->tx_ring_size;
+		vaddr += sizeof(struct txbd8) * tx_queue->tx_ring_size;
+	}
+
+	/* Start the rx descriptor ring where the tx ring leaves off */
+	for (i = 0; i < priv->num_rx_queues; i++) {
+		rx_queue = priv->rx_queue[i];
+		rx_queue->rx_bd_base = vaddr;
+		rx_queue->rx_bd_dma_base = addr;
+		rx_queue->ndev = ndev;
+		rx_queue->dev = dev;
+		addr  += sizeof(struct rxbd8) * rx_queue->rx_ring_size;
+		vaddr += sizeof(struct rxbd8) * rx_queue->rx_ring_size;
+	}
+
+	/* Setup the skbuff rings */
+	for (i = 0; i < priv->num_tx_queues; i++) {
+		tx_queue = priv->tx_queue[i];
+		tx_queue->tx_skbuff =
+			kmalloc_array(tx_queue->tx_ring_size,
+				      sizeof(*tx_queue->tx_skbuff),
+				      GFP_KERNEL);
+		if (!tx_queue->tx_skbuff)
+			goto cleanup;
+
+		for (j = 0; j < tx_queue->tx_ring_size; j++)
+			tx_queue->tx_skbuff[j] = NULL;
+	}
+
+	for (i = 0; i < priv->num_rx_queues; i++) {
+		rx_queue = priv->rx_queue[i];
+		rx_queue->rx_buff = kcalloc(rx_queue->rx_ring_size,
+					    sizeof(*rx_queue->rx_buff),
+					    GFP_KERNEL);
+		if (!rx_queue->rx_buff)
+			goto cleanup;
+	}
+
+	gfar_init_bds(ndev);
+
 	return 0;
+
+cleanup:
+	free_skb_resources(priv);
+	return -ENOMEM;
 }
 
 /* Bring the controller up and running */
@@ -2232,27 +1469,247 @@ int startup_gfar(struct net_device *ndev)
 	return 0;
 }
 
-/* Called when something needs to use the ethernet device
- * Returns 0 for success.
+static u32 gfar_get_flowctrl_cfg(struct gfar_private *priv)
+{
+	struct net_device *ndev = priv->ndev;
+	struct phy_device *phydev = ndev->phydev;
+	u32 val = 0;
+
+	if (!phydev->duplex)
+		return val;
+
+	if (!priv->pause_aneg_en) {
+		if (priv->tx_pause_en)
+			val |= MACCFG1_TX_FLOW;
+		if (priv->rx_pause_en)
+			val |= MACCFG1_RX_FLOW;
+	} else {
+		u16 lcl_adv, rmt_adv;
+		u8 flowctrl;
+		/* get link partner capabilities */
+		rmt_adv = 0;
+		if (phydev->pause)
+			rmt_adv = LPA_PAUSE_CAP;
+		if (phydev->asym_pause)
+			rmt_adv |= LPA_PAUSE_ASYM;
+
+		lcl_adv = linkmode_adv_to_lcl_adv_t(phydev->advertising);
+		flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
+		if (flowctrl & FLOW_CTRL_TX)
+			val |= MACCFG1_TX_FLOW;
+		if (flowctrl & FLOW_CTRL_RX)
+			val |= MACCFG1_RX_FLOW;
+	}
+
+	return val;
+}
+
+static noinline void gfar_update_link_state(struct gfar_private *priv)
+{
+	struct gfar __iomem *regs = priv->gfargrp[0].regs;
+	struct net_device *ndev = priv->ndev;
+	struct phy_device *phydev = ndev->phydev;
+	struct gfar_priv_rx_q *rx_queue = NULL;
+	int i;
+
+	if (unlikely(test_bit(GFAR_RESETTING, &priv->state)))
+		return;
+
+	if (phydev->link) {
+		u32 tempval1 = gfar_read(&regs->maccfg1);
+		u32 tempval = gfar_read(&regs->maccfg2);
+		u32 ecntrl = gfar_read(&regs->ecntrl);
+		u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW);
+
+		if (phydev->duplex != priv->oldduplex) {
+			if (!(phydev->duplex))
+				tempval &= ~(MACCFG2_FULL_DUPLEX);
+			else
+				tempval |= MACCFG2_FULL_DUPLEX;
+
+			priv->oldduplex = phydev->duplex;
+		}
+
+		if (phydev->speed != priv->oldspeed) {
+			switch (phydev->speed) {
+			case 1000:
+				tempval =
+				    ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
+
+				ecntrl &= ~(ECNTRL_R100);
+				break;
+			case 100:
+			case 10:
+				tempval =
+				    ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
+
+				/* Reduced mode distinguishes
+				 * between 10 and 100
+				 */
+				if (phydev->speed == SPEED_100)
+					ecntrl |= ECNTRL_R100;
+				else
+					ecntrl &= ~(ECNTRL_R100);
+				break;
+			default:
+				netif_warn(priv, link, priv->ndev,
+					   "Ack!  Speed (%d) is not 10/100/1000!\n",
+					   phydev->speed);
+				break;
+			}
+
+			priv->oldspeed = phydev->speed;
+		}
+
+		tempval1 &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
+		tempval1 |= gfar_get_flowctrl_cfg(priv);
+
+		/* Turn last free buffer recording on */
+		if ((tempval1 & MACCFG1_TX_FLOW) && !tx_flow_oldval) {
+			for (i = 0; i < priv->num_rx_queues; i++) {
+				u32 bdp_dma;
+
+				rx_queue = priv->rx_queue[i];
+				bdp_dma = gfar_rxbd_dma_lastfree(rx_queue);
+				gfar_write(rx_queue->rfbptr, bdp_dma);
+			}
+
+			priv->tx_actual_en = 1;
+		}
+
+		if (unlikely(!(tempval1 & MACCFG1_TX_FLOW) && tx_flow_oldval))
+			priv->tx_actual_en = 0;
+
+		gfar_write(&regs->maccfg1, tempval1);
+		gfar_write(&regs->maccfg2, tempval);
+		gfar_write(&regs->ecntrl, ecntrl);
+
+		if (!priv->oldlink)
+			priv->oldlink = 1;
+
+	} else if (priv->oldlink) {
+		priv->oldlink = 0;
+		priv->oldspeed = 0;
+		priv->oldduplex = -1;
+	}
+
+	if (netif_msg_link(priv))
+		phy_print_status(phydev);
+}
+
+/* Called every time the controller might need to be made
+ * aware of new link state.  The PHY code conveys this
+ * information through variables in the phydev structure, and this
+ * function converts those variables into the appropriate
+ * register values, and can bring down the device if needed.
  */
-static int gfar_enet_open(struct net_device *dev)
+static void adjust_link(struct net_device *dev)
 {
 	struct gfar_private *priv = netdev_priv(dev);
-	int err;
+	struct phy_device *phydev = dev->phydev;
 
-	err = init_phy(dev);
-	if (err)
-		return err;
+	if (unlikely(phydev->link != priv->oldlink ||
+		     (phydev->link && (phydev->duplex != priv->oldduplex ||
+				       phydev->speed != priv->oldspeed))))
+		gfar_update_link_state(priv);
+}
 
-	err = gfar_request_irq(priv);
-	if (err)
-		return err;
+/* Initialize TBI PHY interface for communicating with the
+ * SERDES lynx PHY on the chip.  We communicate with this PHY
+ * through the MDIO bus on each controller, treating it as a
+ * "normal" PHY at the address found in the TBIPA register.  We assume
+ * that the TBIPA register is valid.  Either the MDIO bus code will set
+ * it to a value that doesn't conflict with other PHYs on the bus, or the
+ * value doesn't matter, as there are no other PHYs on the bus.
+ */
+static void gfar_configure_serdes(struct net_device *dev)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+	struct phy_device *tbiphy;
 
-	err = startup_gfar(dev);
-	if (err)
-		return err;
+	if (!priv->tbi_node) {
+		dev_warn(&dev->dev, "error: SGMII mode requires that the "
+				    "device tree specify a tbi-handle\n");
+		return;
+	}
 
-	return err;
+	tbiphy = of_phy_find_device(priv->tbi_node);
+	if (!tbiphy) {
+		dev_err(&dev->dev, "error: Could not get TBI device\n");
+		return;
+	}
+
+	/* If the link is already up, we must already be ok, and don't need to
+	 * configure and reset the TBI<->SerDes link.  Maybe U-Boot configured
+	 * everything for us?  Resetting it takes the link down and requires
+	 * several seconds for it to come back.
+	 */
+	if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) {
+		put_device(&tbiphy->mdio.dev);
+		return;
+	}
+
+	/* Single clk mode, mii mode off(for serdes communication) */
+	phy_write(tbiphy, MII_TBICON, TBICON_CLK_SELECT);
+
+	phy_write(tbiphy, MII_ADVERTISE,
+		  ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE |
+		  ADVERTISE_1000XPSE_ASYM);
+
+	phy_write(tbiphy, MII_BMCR,
+		  BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX |
+		  BMCR_SPEED1000);
+
+	put_device(&tbiphy->mdio.dev);
+}
+
+/* Initializes driver's PHY state, and attaches to the PHY.
+ * Returns 0 on success.
+ */
+static int init_phy(struct net_device *dev)
+{
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+	struct gfar_private *priv = netdev_priv(dev);
+	phy_interface_t interface;
+	struct phy_device *phydev;
+	struct ethtool_eee edata;
+
+	linkmode_set_bit_array(phy_10_100_features_array,
+			       ARRAY_SIZE(phy_10_100_features_array),
+			       mask);
+	linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask);
+	linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, mask);
+	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
+		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mask);
+
+	priv->oldlink = 0;
+	priv->oldspeed = 0;
+	priv->oldduplex = -1;
+
+	interface = gfar_get_interface(dev);
+
+	phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0,
+				interface);
+	if (!phydev) {
+		dev_err(&dev->dev, "could not attach to PHY\n");
+		return -ENODEV;
+	}
+
+	if (interface == PHY_INTERFACE_MODE_SGMII)
+		gfar_configure_serdes(dev);
+
+	/* Remove any features not supported by the controller */
+	linkmode_and(phydev->supported, phydev->supported, mask);
+	linkmode_copy(phydev->advertising, phydev->supported);
+
+	/* Add support for flow control */
+	phy_support_asym_pause(phydev);
+
+	/* disable EEE autoneg, EEE not supported by eTSEC */
+	memset(&edata, 0, sizeof(struct ethtool_eee));
+	phy_ethtool_set_eee(phydev, &edata);
+
+	return 0;
 }
 
 static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb)
@@ -2583,22 +2040,6 @@ static netdev_tx_t gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	return NETDEV_TX_OK;
 }
 
-/* Stops the kernel queue, and halts the controller */
-static int gfar_close(struct net_device *dev)
-{
-	struct gfar_private *priv = netdev_priv(dev);
-
-	cancel_work_sync(&priv->reset_task);
-	stop_gfar(dev);
-
-	/* Disconnect from the PHY */
-	phy_disconnect(dev->phydev);
-
-	gfar_free_irq(priv);
-
-	return 0;
-}
-
 /* Changes the mac address if the controller is not running. */
 static int gfar_set_mac_address(struct net_device *dev)
 {
@@ -2660,6 +2101,85 @@ static void gfar_timeout(struct net_device *dev)
 	schedule_work(&priv->reset_task);
 }
 
+static int gfar_hwtstamp_set(struct net_device *netdev, struct ifreq *ifr)
+{
+	struct hwtstamp_config config;
+	struct gfar_private *priv = netdev_priv(netdev);
+
+	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+		return -EFAULT;
+
+	/* reserved for future extensions */
+	if (config.flags)
+		return -EINVAL;
+
+	switch (config.tx_type) {
+	case HWTSTAMP_TX_OFF:
+		priv->hwts_tx_en = 0;
+		break;
+	case HWTSTAMP_TX_ON:
+		if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER))
+			return -ERANGE;
+		priv->hwts_tx_en = 1;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	switch (config.rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		if (priv->hwts_rx_en) {
+			priv->hwts_rx_en = 0;
+			reset_gfar(netdev);
+		}
+		break;
+	default:
+		if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER))
+			return -ERANGE;
+		if (!priv->hwts_rx_en) {
+			priv->hwts_rx_en = 1;
+			reset_gfar(netdev);
+		}
+		config.rx_filter = HWTSTAMP_FILTER_ALL;
+		break;
+	}
+
+	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+		-EFAULT : 0;
+}
+
+static int gfar_hwtstamp_get(struct net_device *netdev, struct ifreq *ifr)
+{
+	struct hwtstamp_config config;
+	struct gfar_private *priv = netdev_priv(netdev);
+
+	config.flags = 0;
+	config.tx_type = priv->hwts_tx_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+	config.rx_filter = (priv->hwts_rx_en ?
+			    HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE);
+
+	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+		-EFAULT : 0;
+}
+
+static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct phy_device *phydev = dev->phydev;
+
+	if (!netif_running(dev))
+		return -EINVAL;
+
+	if (cmd == SIOCSHWTSTAMP)
+		return gfar_hwtstamp_set(dev, rq);
+	if (cmd == SIOCGHWTSTAMP)
+		return gfar_hwtstamp_get(dev, rq);
+
+	if (!phydev)
+		return -ENODEV;
+
+	return phy_mii_ioctl(phydev, rq, cmd);
+}
+
 /* Interrupt Handler for Transmit complete */
 static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
 {
@@ -2767,77 +2287,6 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
 	netdev_tx_completed_queue(txq, howmany, bytes_sent);
 }
 
-static bool gfar_new_page(struct gfar_priv_rx_q *rxq, struct gfar_rx_buff *rxb)
-{
-	struct page *page;
-	dma_addr_t addr;
-
-	page = dev_alloc_page();
-	if (unlikely(!page))
-		return false;
-
-	addr = dma_map_page(rxq->dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
-	if (unlikely(dma_mapping_error(rxq->dev, addr))) {
-		__free_page(page);
-
-		return false;
-	}
-
-	rxb->dma = addr;
-	rxb->page = page;
-	rxb->page_offset = 0;
-
-	return true;
-}
-
-static void gfar_rx_alloc_err(struct gfar_priv_rx_q *rx_queue)
-{
-	struct gfar_private *priv = netdev_priv(rx_queue->ndev);
-	struct gfar_extra_stats *estats = &priv->extra_stats;
-
-	netdev_err(rx_queue->ndev, "Can't alloc RX buffers\n");
-	atomic64_inc(&estats->rx_alloc_err);
-}
-
-static void gfar_alloc_rx_buffs(struct gfar_priv_rx_q *rx_queue,
-				int alloc_cnt)
-{
-	struct rxbd8 *bdp;
-	struct gfar_rx_buff *rxb;
-	int i;
-
-	i = rx_queue->next_to_use;
-	bdp = &rx_queue->rx_bd_base[i];
-	rxb = &rx_queue->rx_buff[i];
-
-	while (alloc_cnt--) {
-		/* try reuse page */
-		if (unlikely(!rxb->page)) {
-			if (unlikely(!gfar_new_page(rx_queue, rxb))) {
-				gfar_rx_alloc_err(rx_queue);
-				break;
-			}
-		}
-
-		/* Setup the new RxBD */
-		gfar_init_rxbdp(rx_queue, bdp,
-				rxb->dma + rxb->page_offset + RXBUF_ALIGNMENT);
-
-		/* Update to the next pointer */
-		bdp++;
-		rxb++;
-
-		if (unlikely(++i == rx_queue->rx_ring_size)) {
-			i = 0;
-			bdp = rx_queue->rx_bd_base;
-			rxb = rx_queue->rx_buff;
-		}
-	}
-
-	rx_queue->next_to_use = i;
-	rx_queue->next_to_alloc = i;
-}
-
 static void count_errors(u32 lstatus, struct net_device *ndev)
 {
 	struct gfar_private *priv = netdev_priv(ndev);
@@ -3327,6 +2776,98 @@ static int gfar_poll_tx(struct napi_struct *napi, int budget)
 	return 0;
 }
 
+/* GFAR error interrupt handler */
+static irqreturn_t gfar_error(int irq, void *grp_id)
+{
+	struct gfar_priv_grp *gfargrp = grp_id;
+	struct gfar __iomem *regs = gfargrp->regs;
+	struct gfar_private *priv= gfargrp->priv;
+	struct net_device *dev = priv->ndev;
+
+	/* Save ievent for future reference */
+	u32 events = gfar_read(&regs->ievent);
+
+	/* Clear IEVENT */
+	gfar_write(&regs->ievent, events & IEVENT_ERR_MASK);
+
+	/* Magic Packet is not an error. */
+	if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
+	    (events & IEVENT_MAG))
+		events &= ~IEVENT_MAG;
+
+	/* Hmm... */
+	if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv))
+		netdev_dbg(dev,
+			   "error interrupt (ievent=0x%08x imask=0x%08x)\n",
+			   events, gfar_read(&regs->imask));
+
+	/* Update the error counters */
+	if (events & IEVENT_TXE) {
+		dev->stats.tx_errors++;
+
+		if (events & IEVENT_LC)
+			dev->stats.tx_window_errors++;
+		if (events & IEVENT_CRL)
+			dev->stats.tx_aborted_errors++;
+		if (events & IEVENT_XFUN) {
+			netif_dbg(priv, tx_err, dev,
+				  "TX FIFO underrun, packet dropped\n");
+			dev->stats.tx_dropped++;
+			atomic64_inc(&priv->extra_stats.tx_underrun);
+
+			schedule_work(&priv->reset_task);
+		}
+		netif_dbg(priv, tx_err, dev, "Transmit Error\n");
+	}
+	if (events & IEVENT_BSY) {
+		dev->stats.rx_over_errors++;
+		atomic64_inc(&priv->extra_stats.rx_bsy);
+
+		netif_dbg(priv, rx_err, dev, "busy error (rstat: %x)\n",
+			  gfar_read(&regs->rstat));
+	}
+	if (events & IEVENT_BABR) {
+		dev->stats.rx_errors++;
+		atomic64_inc(&priv->extra_stats.rx_babr);
+
+		netif_dbg(priv, rx_err, dev, "babbling RX error\n");
+	}
+	if (events & IEVENT_EBERR) {
+		atomic64_inc(&priv->extra_stats.eberr);
+		netif_dbg(priv, rx_err, dev, "bus error\n");
+	}
+	if (events & IEVENT_RXC)
+		netif_dbg(priv, rx_status, dev, "control frame\n");
+
+	if (events & IEVENT_BABT) {
+		atomic64_inc(&priv->extra_stats.tx_babt);
+		netif_dbg(priv, tx_err, dev, "babbling TX error\n");
+	}
+	return IRQ_HANDLED;
+}
+
+/* The interrupt handler for devices with one interrupt */
+static irqreturn_t gfar_interrupt(int irq, void *grp_id)
+{
+	struct gfar_priv_grp *gfargrp = grp_id;
+
+	/* Save ievent for future reference */
+	u32 events = gfar_read(&gfargrp->regs->ievent);
+
+	/* Check for reception */
+	if (events & IEVENT_RX_MASK)
+		gfar_receive(irq, grp_id);
+
+	/* Check for transmit completion */
+	if (events & IEVENT_TX_MASK)
+		gfar_transmit(irq, grp_id);
+
+	/* Check for errors */
+	if (events & IEVENT_ERR_MASK)
+		gfar_error(irq, grp_id);
+
+	return IRQ_HANDLED;
+}
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
 /* Polling 'interrupt' - used by things like netconsole to send skbs
@@ -3363,44 +2904,154 @@ static void gfar_netpoll(struct net_device *dev)
 }
 #endif
 
-/* The interrupt handler for devices with one interrupt */
-static irqreturn_t gfar_interrupt(int irq, void *grp_id)
+static void free_grp_irqs(struct gfar_priv_grp *grp)
 {
-	struct gfar_priv_grp *gfargrp = grp_id;
-
-	/* Save ievent for future reference */
-	u32 events = gfar_read(&gfargrp->regs->ievent);
-
-	/* Check for reception */
-	if (events & IEVENT_RX_MASK)
-		gfar_receive(irq, grp_id);
-
-	/* Check for transmit completion */
-	if (events & IEVENT_TX_MASK)
-		gfar_transmit(irq, grp_id);
-
-	/* Check for errors */
-	if (events & IEVENT_ERR_MASK)
-		gfar_error(irq, grp_id);
-
-	return IRQ_HANDLED;
+	free_irq(gfar_irq(grp, TX)->irq, grp);
+	free_irq(gfar_irq(grp, RX)->irq, grp);
+	free_irq(gfar_irq(grp, ER)->irq, grp);
 }
 
-/* Called every time the controller might need to be made
- * aware of new link state.  The PHY code conveys this
- * information through variables in the phydev structure, and this
- * function converts those variables into the appropriate
- * register values, and can bring down the device if needed.
+static int register_grp_irqs(struct gfar_priv_grp *grp)
+{
+	struct gfar_private *priv = grp->priv;
+	struct net_device *dev = priv->ndev;
+	int err;
+
+	/* If the device has multiple interrupts, register for
+	 * them.  Otherwise, only register for the one
+	 */
+	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+		/* Install our interrupt handlers for Error,
+		 * Transmit, and Receive
+		 */
+		err = request_irq(gfar_irq(grp, ER)->irq, gfar_error, 0,
+				  gfar_irq(grp, ER)->name, grp);
+		if (err < 0) {
+			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
+				  gfar_irq(grp, ER)->irq);
+
+			goto err_irq_fail;
+		}
+		enable_irq_wake(gfar_irq(grp, ER)->irq);
+
+		err = request_irq(gfar_irq(grp, TX)->irq, gfar_transmit, 0,
+				  gfar_irq(grp, TX)->name, grp);
+		if (err < 0) {
+			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
+				  gfar_irq(grp, TX)->irq);
+			goto tx_irq_fail;
+		}
+		err = request_irq(gfar_irq(grp, RX)->irq, gfar_receive, 0,
+				  gfar_irq(grp, RX)->name, grp);
+		if (err < 0) {
+			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
+				  gfar_irq(grp, RX)->irq);
+			goto rx_irq_fail;
+		}
+		enable_irq_wake(gfar_irq(grp, RX)->irq);
+
+	} else {
+		err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt, 0,
+				  gfar_irq(grp, TX)->name, grp);
+		if (err < 0) {
+			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
+				  gfar_irq(grp, TX)->irq);
+			goto err_irq_fail;
+		}
+		enable_irq_wake(gfar_irq(grp, TX)->irq);
+	}
+
+	return 0;
+
+rx_irq_fail:
+	free_irq(gfar_irq(grp, TX)->irq, grp);
+tx_irq_fail:
+	free_irq(gfar_irq(grp, ER)->irq, grp);
+err_irq_fail:
+	return err;
+
+}
+
+static void gfar_free_irq(struct gfar_private *priv)
+{
+	int i;
+
+	/* Free the IRQs */
+	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+		for (i = 0; i < priv->num_grps; i++)
+			free_grp_irqs(&priv->gfargrp[i]);
+	} else {
+		for (i = 0; i < priv->num_grps; i++)
+			free_irq(gfar_irq(&priv->gfargrp[i], TX)->irq,
+				 &priv->gfargrp[i]);
+	}
+}
+
+static int gfar_request_irq(struct gfar_private *priv)
+{
+	int err, i, j;
+
+	for (i = 0; i < priv->num_grps; i++) {
+		err = register_grp_irqs(&priv->gfargrp[i]);
+		if (err) {
+			for (j = 0; j < i; j++)
+				free_grp_irqs(&priv->gfargrp[j]);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+/* Called when something needs to use the ethernet device
+ * Returns 0 for success.
  */
-static void adjust_link(struct net_device *dev)
+static int gfar_enet_open(struct net_device *dev)
 {
 	struct gfar_private *priv = netdev_priv(dev);
-	struct phy_device *phydev = dev->phydev;
+	int err;
 
-	if (unlikely(phydev->link != priv->oldlink ||
-		     (phydev->link && (phydev->duplex != priv->oldduplex ||
-				       phydev->speed != priv->oldspeed))))
-		gfar_update_link_state(priv);
+	err = init_phy(dev);
+	if (err)
+		return err;
+
+	err = gfar_request_irq(priv);
+	if (err)
+		return err;
+
+	err = startup_gfar(dev);
+	if (err)
+		return err;
+
+	return err;
+}
+
+/* Stops the kernel queue, and halts the controller */
+static int gfar_close(struct net_device *dev)
+{
+	struct gfar_private *priv = netdev_priv(dev);
+
+	cancel_work_sync(&priv->reset_task);
+	stop_gfar(dev);
+
+	/* Disconnect from the PHY */
+	phy_disconnect(dev->phydev);
+
+	gfar_free_irq(priv);
+
+	return 0;
+}
+
+/* Clears each of the exact match registers to zero, so they
+ * don't interfere with normal reception
+ */
+static void gfar_clear_exact_match(struct net_device *dev)
+{
+	int idx;
+	static const u8 zero_arr[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
+
+	for (idx = 1; idx < GFAR_EM_NUM + 1; idx++)
+		gfar_set_mac_for_addr(dev, idx, zero_arr);
 }
 
 /* Update the hash table based on the current list of multicast
@@ -3494,274 +3145,582 @@ static void gfar_set_multi(struct net_device *dev)
 	}
 }
 
-
-/* Clears each of the exact match registers to zero, so they
- * don't interfere with normal reception
- */
-static void gfar_clear_exact_match(struct net_device *dev)
+void gfar_mac_reset(struct gfar_private *priv)
 {
-	int idx;
-	static const u8 zero_arr[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
-
-	for (idx = 1; idx < GFAR_EM_NUM + 1; idx++)
-		gfar_set_mac_for_addr(dev, idx, zero_arr);
-}
-
-/* Set the appropriate hash bit for the given addr */
-/* The algorithm works like so:
- * 1) Take the Destination Address (ie the multicast address), and
- * do a CRC on it (little endian), and reverse the bits of the
- * result.
- * 2) Use the 8 most significant bits as a hash into a 256-entry
- * table.  The table is controlled through 8 32-bit registers:
- * gaddr0-7.  gaddr0's MSB is entry 0, and gaddr7's LSB is
- * gaddr7.  This means that the 3 most significant bits in the
- * hash index which gaddr register to use, and the 5 other bits
- * indicate which bit (assuming an IBM numbering scheme, which
- * for PowerPC (tm) is usually the case) in the register holds
- * the entry.
- */
-static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr)
-{
-	u32 tempval;
-	struct gfar_private *priv = netdev_priv(dev);
-	u32 result = ether_crc(ETH_ALEN, addr);
-	int width = priv->hash_width;
-	u8 whichbit = (result >> (32 - width)) & 0x1f;
-	u8 whichreg = result >> (32 - width + 5);
-	u32 value = (1 << (31-whichbit));
-
-	tempval = gfar_read(priv->hash_regs[whichreg]);
-	tempval |= value;
-	gfar_write(priv->hash_regs[whichreg], tempval);
-}
-
-
-/* There are multiple MAC Address register pairs on some controllers
- * This function sets the numth pair to a given address
- */
-static void gfar_set_mac_for_addr(struct net_device *dev, int num,
-				  const u8 *addr)
-{
-	struct gfar_private *priv = netdev_priv(dev);
 	struct gfar __iomem *regs = priv->gfargrp[0].regs;
 	u32 tempval;
-	u32 __iomem *macptr = &regs->macstnaddr1;
 
-	macptr += num*2;
+	/* Reset MAC layer */
+	gfar_write(&regs->maccfg1, MACCFG1_SOFT_RESET);
 
-	/* For a station address of 0x12345678ABCD in transmission
-	 * order (BE), MACnADDR1 is set to 0xCDAB7856 and
-	 * MACnADDR2 is set to 0x34120000.
+	/* We need to delay at least 3 TX clocks */
+	udelay(3);
+
+	/* the soft reset bit is not self-resetting, so we need to
+	 * clear it before resuming normal operation
 	 */
-	tempval = (addr[5] << 24) | (addr[4] << 16) |
-		  (addr[3] << 8)  |  addr[2];
+	gfar_write(&regs->maccfg1, 0);
 
-	gfar_write(macptr, tempval);
+	udelay(3);
 
-	tempval = (addr[1] << 24) | (addr[0] << 16);
+	gfar_rx_offload_en(priv);
 
-	gfar_write(macptr+1, tempval);
+	/* Initialize the max receive frame/buffer lengths */
+	gfar_write(&regs->maxfrm, GFAR_JUMBO_FRAME_SIZE);
+	gfar_write(&regs->mrblr, GFAR_RXB_SIZE);
+
+	/* Initialize the Minimum Frame Length Register */
+	gfar_write(&regs->minflr, MINFLR_INIT_SETTINGS);
+
+	/* Initialize MACCFG2. */
+	tempval = MACCFG2_INIT_SETTINGS;
+
+	/* eTSEC74 erratum: Rx frames of length MAXFRM or MAXFRM-1
+	 * are marked as truncated.  Avoid this by MACCFG2[Huge Frame]=1,
+	 * and by checking RxBD[LG] and discarding larger than MAXFRM.
+	 */
+	if (gfar_has_errata(priv, GFAR_ERRATA_74))
+		tempval |= MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK;
+
+	gfar_write(&regs->maccfg2, tempval);
+
+	/* Clear mac addr hash registers */
+	gfar_write(&regs->igaddr0, 0);
+	gfar_write(&regs->igaddr1, 0);
+	gfar_write(&regs->igaddr2, 0);
+	gfar_write(&regs->igaddr3, 0);
+	gfar_write(&regs->igaddr4, 0);
+	gfar_write(&regs->igaddr5, 0);
+	gfar_write(&regs->igaddr6, 0);
+	gfar_write(&regs->igaddr7, 0);
+
+	gfar_write(&regs->gaddr0, 0);
+	gfar_write(&regs->gaddr1, 0);
+	gfar_write(&regs->gaddr2, 0);
+	gfar_write(&regs->gaddr3, 0);
+	gfar_write(&regs->gaddr4, 0);
+	gfar_write(&regs->gaddr5, 0);
+	gfar_write(&regs->gaddr6, 0);
+	gfar_write(&regs->gaddr7, 0);
+
+	if (priv->extended_hash)
+		gfar_clear_exact_match(priv->ndev);
+
+	gfar_mac_rx_config(priv);
+
+	gfar_mac_tx_config(priv);
+
+	gfar_set_mac_address(priv->ndev);
+
+	gfar_set_multi(priv->ndev);
+
+	/* clear ievent and imask before configuring coalescing */
+	gfar_ints_disable(priv);
+
+	/* Configure the coalescing support */
+	gfar_configure_coalescing_all(priv);
 }
 
-/* GFAR error interrupt handler */
-static irqreturn_t gfar_error(int irq, void *grp_id)
-{
-	struct gfar_priv_grp *gfargrp = grp_id;
-	struct gfar __iomem *regs = gfargrp->regs;
-	struct gfar_private *priv= gfargrp->priv;
-	struct net_device *dev = priv->ndev;
-
-	/* Save ievent for future reference */
-	u32 events = gfar_read(&regs->ievent);
-
-	/* Clear IEVENT */
-	gfar_write(&regs->ievent, events & IEVENT_ERR_MASK);
-
-	/* Magic Packet is not an error. */
-	if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
-	    (events & IEVENT_MAG))
-		events &= ~IEVENT_MAG;
-
-	/* Hmm... */
-	if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv))
-		netdev_dbg(dev,
-			   "error interrupt (ievent=0x%08x imask=0x%08x)\n",
-			   events, gfar_read(&regs->imask));
-
-	/* Update the error counters */
-	if (events & IEVENT_TXE) {
-		dev->stats.tx_errors++;
-
-		if (events & IEVENT_LC)
-			dev->stats.tx_window_errors++;
-		if (events & IEVENT_CRL)
-			dev->stats.tx_aborted_errors++;
-		if (events & IEVENT_XFUN) {
-			netif_dbg(priv, tx_err, dev,
-				  "TX FIFO underrun, packet dropped\n");
-			dev->stats.tx_dropped++;
-			atomic64_inc(&priv->extra_stats.tx_underrun);
-
-			schedule_work(&priv->reset_task);
-		}
-		netif_dbg(priv, tx_err, dev, "Transmit Error\n");
-	}
-	if (events & IEVENT_BSY) {
-		dev->stats.rx_over_errors++;
-		atomic64_inc(&priv->extra_stats.rx_bsy);
-
-		netif_dbg(priv, rx_err, dev, "busy error (rstat: %x)\n",
-			  gfar_read(&regs->rstat));
-	}
-	if (events & IEVENT_BABR) {
-		dev->stats.rx_errors++;
-		atomic64_inc(&priv->extra_stats.rx_babr);
-
-		netif_dbg(priv, rx_err, dev, "babbling RX error\n");
-	}
-	if (events & IEVENT_EBERR) {
-		atomic64_inc(&priv->extra_stats.eberr);
-		netif_dbg(priv, rx_err, dev, "bus error\n");
-	}
-	if (events & IEVENT_RXC)
-		netif_dbg(priv, rx_status, dev, "control frame\n");
-
-	if (events & IEVENT_BABT) {
-		atomic64_inc(&priv->extra_stats.tx_babt);
-		netif_dbg(priv, tx_err, dev, "babbling TX error\n");
-	}
-	return IRQ_HANDLED;
-}
-
-static u32 gfar_get_flowctrl_cfg(struct gfar_private *priv)
-{
-	struct net_device *ndev = priv->ndev;
-	struct phy_device *phydev = ndev->phydev;
-	u32 val = 0;
-
-	if (!phydev->duplex)
-		return val;
-
-	if (!priv->pause_aneg_en) {
-		if (priv->tx_pause_en)
-			val |= MACCFG1_TX_FLOW;
-		if (priv->rx_pause_en)
-			val |= MACCFG1_RX_FLOW;
-	} else {
-		u16 lcl_adv, rmt_adv;
-		u8 flowctrl;
-		/* get link partner capabilities */
-		rmt_adv = 0;
-		if (phydev->pause)
-			rmt_adv = LPA_PAUSE_CAP;
-		if (phydev->asym_pause)
-			rmt_adv |= LPA_PAUSE_ASYM;
-
-		lcl_adv = linkmode_adv_to_lcl_adv_t(phydev->advertising);
-		flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
-		if (flowctrl & FLOW_CTRL_TX)
-			val |= MACCFG1_TX_FLOW;
-		if (flowctrl & FLOW_CTRL_RX)
-			val |= MACCFG1_RX_FLOW;
-	}
-
-	return val;
-}
-
-static noinline void gfar_update_link_state(struct gfar_private *priv)
+static void gfar_hw_init(struct gfar_private *priv)
 {
 	struct gfar __iomem *regs = priv->gfargrp[0].regs;
-	struct net_device *ndev = priv->ndev;
-	struct phy_device *phydev = ndev->phydev;
-	struct gfar_priv_rx_q *rx_queue = NULL;
-	int i;
+	u32 attrs;
 
-	if (unlikely(test_bit(GFAR_RESETTING, &priv->state)))
-		return;
+	/* Stop the DMA engine now, in case it was running before
+	 * (The firmware could have used it, and left it running).
+	 */
+	gfar_halt(priv);
 
-	if (phydev->link) {
-		u32 tempval1 = gfar_read(&regs->maccfg1);
-		u32 tempval = gfar_read(&regs->maccfg2);
-		u32 ecntrl = gfar_read(&regs->ecntrl);
-		u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW);
+	gfar_mac_reset(priv);
 
-		if (phydev->duplex != priv->oldduplex) {
-			if (!(phydev->duplex))
-				tempval &= ~(MACCFG2_FULL_DUPLEX);
-			else
-				tempval |= MACCFG2_FULL_DUPLEX;
+	/* Zero out the rmon mib registers if it has them */
+	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
+		memset_io(&(regs->rmon), 0, sizeof(struct rmon_mib));
 
-			priv->oldduplex = phydev->duplex;
-		}
-
-		if (phydev->speed != priv->oldspeed) {
-			switch (phydev->speed) {
-			case 1000:
-				tempval =
-				    ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
-
-				ecntrl &= ~(ECNTRL_R100);
-				break;
-			case 100:
-			case 10:
-				tempval =
-				    ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
-
-				/* Reduced mode distinguishes
-				 * between 10 and 100
-				 */
-				if (phydev->speed == SPEED_100)
-					ecntrl |= ECNTRL_R100;
-				else
-					ecntrl &= ~(ECNTRL_R100);
-				break;
-			default:
-				netif_warn(priv, link, priv->ndev,
-					   "Ack!  Speed (%d) is not 10/100/1000!\n",
-					   phydev->speed);
-				break;
-			}
-
-			priv->oldspeed = phydev->speed;
-		}
-
-		tempval1 &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
-		tempval1 |= gfar_get_flowctrl_cfg(priv);
-
-		/* Turn last free buffer recording on */
-		if ((tempval1 & MACCFG1_TX_FLOW) && !tx_flow_oldval) {
-			for (i = 0; i < priv->num_rx_queues; i++) {
-				u32 bdp_dma;
-
-				rx_queue = priv->rx_queue[i];
-				bdp_dma = gfar_rxbd_dma_lastfree(rx_queue);
-				gfar_write(rx_queue->rfbptr, bdp_dma);
-			}
-
-			priv->tx_actual_en = 1;
-		}
-
-		if (unlikely(!(tempval1 & MACCFG1_TX_FLOW) && tx_flow_oldval))
-			priv->tx_actual_en = 0;
-
-		gfar_write(&regs->maccfg1, tempval1);
-		gfar_write(&regs->maccfg2, tempval);
-		gfar_write(&regs->ecntrl, ecntrl);
-
-		if (!priv->oldlink)
-			priv->oldlink = 1;
-
-	} else if (priv->oldlink) {
-		priv->oldlink = 0;
-		priv->oldspeed = 0;
-		priv->oldduplex = -1;
+		/* Mask off the CAM interrupts */
+		gfar_write(&regs->rmon.cam1, 0xffffffff);
+		gfar_write(&regs->rmon.cam2, 0xffffffff);
 	}
 
-	if (netif_msg_link(priv))
-		phy_print_status(phydev);
+	/* Initialize ECNTRL */
+	gfar_write(&regs->ecntrl, ECNTRL_INIT_SETTINGS);
+
+	/* Set the extraction length and index */
+	attrs = ATTRELI_EL(priv->rx_stash_size) |
+		ATTRELI_EI(priv->rx_stash_index);
+
+	gfar_write(&regs->attreli, attrs);
+
+	/* Start with defaults, and add stashing
+	 * depending on driver parameters
+	 */
+	attrs = ATTR_INIT_SETTINGS;
+
+	if (priv->bd_stash_en)
+		attrs |= ATTR_BDSTASH;
+
+	if (priv->rx_stash_size != 0)
+		attrs |= ATTR_BUFSTASH;
+
+	gfar_write(&regs->attr, attrs);
+
+	/* FIFO configs */
+	gfar_write(&regs->fifo_tx_thr, DEFAULT_FIFO_TX_THR);
+	gfar_write(&regs->fifo_tx_starve, DEFAULT_FIFO_TX_STARVE);
+	gfar_write(&regs->fifo_tx_starve_shutoff, DEFAULT_FIFO_TX_STARVE_OFF);
+
+	/* Program the interrupt steering regs, only for MG devices */
+	if (priv->num_grps > 1)
+		gfar_write_isrg(priv);
 }
 
+static const struct net_device_ops gfar_netdev_ops = {
+	.ndo_open = gfar_enet_open,
+	.ndo_start_xmit = gfar_start_xmit,
+	.ndo_stop = gfar_close,
+	.ndo_change_mtu = gfar_change_mtu,
+	.ndo_set_features = gfar_set_features,
+	.ndo_set_rx_mode = gfar_set_multi,
+	.ndo_tx_timeout = gfar_timeout,
+	.ndo_do_ioctl = gfar_ioctl,
+	.ndo_get_stats = gfar_get_stats,
+	.ndo_change_carrier = fixed_phy_change_carrier,
+	.ndo_set_mac_address = gfar_set_mac_addr,
+	.ndo_validate_addr = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller = gfar_netpoll,
+#endif
+};
+
+/* Set up the ethernet device structure, private data,
+ * and anything else we need before we start
+ */
+static int gfar_probe(struct platform_device *ofdev)
+{
+	struct device_node *np = ofdev->dev.of_node;
+	struct net_device *dev = NULL;
+	struct gfar_private *priv = NULL;
+	int err = 0, i;
+
+	err = gfar_of_init(ofdev, &dev);
+
+	if (err)
+		return err;
+
+	priv = netdev_priv(dev);
+	priv->ndev = dev;
+	priv->ofdev = ofdev;
+	priv->dev = &ofdev->dev;
+	SET_NETDEV_DEV(dev, &ofdev->dev);
+
+	INIT_WORK(&priv->reset_task, gfar_reset_task);
+
+	platform_set_drvdata(ofdev, priv);
+
+	gfar_detect_errata(priv);
+
+	/* Set the dev->base_addr to the gfar reg region */
+	dev->base_addr = (unsigned long) priv->gfargrp[0].regs;
+
+	/* Fill in the dev structure */
+	dev->watchdog_timeo = TX_TIMEOUT;
+	/* MTU range: 50 - 9586 */
+	dev->mtu = 1500;
+	dev->min_mtu = 50;
+	dev->max_mtu = GFAR_JUMBO_FRAME_SIZE - ETH_HLEN;
+	dev->netdev_ops = &gfar_netdev_ops;
+	dev->ethtool_ops = &gfar_ethtool_ops;
+
+	/* Register for napi ...We are registering NAPI for each grp */
+	for (i = 0; i < priv->num_grps; i++) {
+		if (priv->poll_mode == GFAR_SQ_POLLING) {
+			netif_napi_add(dev, &priv->gfargrp[i].napi_rx,
+				       gfar_poll_rx_sq, GFAR_DEV_WEIGHT);
+			netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx,
+				       gfar_poll_tx_sq, 2);
+		} else {
+			netif_napi_add(dev, &priv->gfargrp[i].napi_rx,
+				       gfar_poll_rx, GFAR_DEV_WEIGHT);
+			netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx,
+				       gfar_poll_tx, 2);
+		}
+	}
+
+	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
+		dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
+				   NETIF_F_RXCSUM;
+		dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG |
+				 NETIF_F_RXCSUM | NETIF_F_HIGHDMA;
+	}
+
+	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
+		dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX |
+				    NETIF_F_HW_VLAN_CTAG_RX;
+		dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
+	}
+
+	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+
+	gfar_init_addr_hash_table(priv);
+
+	/* Insert receive time stamps into padding alignment bytes, and
+	 * plus 2 bytes padding to ensure the cpu alignment.
+	 */
+	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)
+		priv->padding = 8 + DEFAULT_PADDING;
+
+	if (dev->features & NETIF_F_IP_CSUM ||
+	    priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)
+		dev->needed_headroom = GMAC_FCB_LEN;
+
+	/* Initializing some of the rx/tx queue level parameters */
+	for (i = 0; i < priv->num_tx_queues; i++) {
+		priv->tx_queue[i]->tx_ring_size = DEFAULT_TX_RING_SIZE;
+		priv->tx_queue[i]->num_txbdfree = DEFAULT_TX_RING_SIZE;
+		priv->tx_queue[i]->txcoalescing = DEFAULT_TX_COALESCE;
+		priv->tx_queue[i]->txic = DEFAULT_TXIC;
+	}
+
+	for (i = 0; i < priv->num_rx_queues; i++) {
+		priv->rx_queue[i]->rx_ring_size = DEFAULT_RX_RING_SIZE;
+		priv->rx_queue[i]->rxcoalescing = DEFAULT_RX_COALESCE;
+		priv->rx_queue[i]->rxic = DEFAULT_RXIC;
+	}
+
+	/* Always enable rx filer if available */
+	priv->rx_filer_enable =
+	    (priv->device_flags & FSL_GIANFAR_DEV_HAS_RX_FILER) ? 1 : 0;
+	/* Enable most messages by default */
+	priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
+	/* use pritority h/w tx queue scheduling for single queue devices */
+	if (priv->num_tx_queues == 1)
+		priv->prio_sched_en = 1;
+
+	set_bit(GFAR_DOWN, &priv->state);
+
+	gfar_hw_init(priv);
+
+	/* Carrier starts down, phylib will bring it up */
+	netif_carrier_off(dev);
+
+	err = register_netdev(dev);
+
+	if (err) {
+		pr_err("%s: Cannot register net device, aborting\n", dev->name);
+		goto register_fail;
+	}
+
+	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET)
+		priv->wol_supported |= GFAR_WOL_MAGIC;
+
+	if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER) &&
+	    priv->rx_filer_enable)
+		priv->wol_supported |= GFAR_WOL_FILER_UCAST;
+
+	device_set_wakeup_capable(&ofdev->dev, priv->wol_supported);
+
+	/* fill out IRQ number and name fields */
+	for (i = 0; i < priv->num_grps; i++) {
+		struct gfar_priv_grp *grp = &priv->gfargrp[i];
+		if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+			sprintf(gfar_irq(grp, TX)->name, "%s%s%c%s",
+				dev->name, "_g", '0' + i, "_tx");
+			sprintf(gfar_irq(grp, RX)->name, "%s%s%c%s",
+				dev->name, "_g", '0' + i, "_rx");
+			sprintf(gfar_irq(grp, ER)->name, "%s%s%c%s",
+				dev->name, "_g", '0' + i, "_er");
+		} else
+			strcpy(gfar_irq(grp, TX)->name, dev->name);
+	}
+
+	/* Initialize the filer table */
+	gfar_init_filer_table(priv);
+
+	/* Print out the device info */
+	netdev_info(dev, "mac: %pM\n", dev->dev_addr);
+
+	/* Even more device info helps when determining which kernel
+	 * provided which set of benchmarks.
+	 */
+	netdev_info(dev, "Running with NAPI enabled\n");
+	for (i = 0; i < priv->num_rx_queues; i++)
+		netdev_info(dev, "RX BD ring size for Q[%d]: %d\n",
+			    i, priv->rx_queue[i]->rx_ring_size);
+	for (i = 0; i < priv->num_tx_queues; i++)
+		netdev_info(dev, "TX BD ring size for Q[%d]: %d\n",
+			    i, priv->tx_queue[i]->tx_ring_size);
+
+	return 0;
+
+register_fail:
+	if (of_phy_is_fixed_link(np))
+		of_phy_deregister_fixed_link(np);
+	unmap_group_regs(priv);
+	gfar_free_rx_queues(priv);
+	gfar_free_tx_queues(priv);
+	of_node_put(priv->phy_node);
+	of_node_put(priv->tbi_node);
+	free_gfar_dev(priv);
+	return err;
+}
+
+static int gfar_remove(struct platform_device *ofdev)
+{
+	struct gfar_private *priv = platform_get_drvdata(ofdev);
+	struct device_node *np = ofdev->dev.of_node;
+
+	of_node_put(priv->phy_node);
+	of_node_put(priv->tbi_node);
+
+	unregister_netdev(priv->ndev);
+
+	if (of_phy_is_fixed_link(np))
+		of_phy_deregister_fixed_link(np);
+
+	unmap_group_regs(priv);
+	gfar_free_rx_queues(priv);
+	gfar_free_tx_queues(priv);
+	free_gfar_dev(priv);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static void __gfar_filer_disable(struct gfar_private *priv)
+{
+	struct gfar __iomem *regs = priv->gfargrp[0].regs;
+	u32 temp;
+
+	temp = gfar_read(&regs->rctrl);
+	temp &= ~(RCTRL_FILREN | RCTRL_PRSDEP_INIT);
+	gfar_write(&regs->rctrl, temp);
+}
+
+static void __gfar_filer_enable(struct gfar_private *priv)
+{
+	struct gfar __iomem *regs = priv->gfargrp[0].regs;
+	u32 temp;
+
+	temp = gfar_read(&regs->rctrl);
+	temp |= RCTRL_FILREN | RCTRL_PRSDEP_INIT;
+	gfar_write(&regs->rctrl, temp);
+}
+
+/* Filer rules implementing wol capabilities */
+static void gfar_filer_config_wol(struct gfar_private *priv)
+{
+	unsigned int i;
+	u32 rqfcr;
+
+	__gfar_filer_disable(priv);
+
+	/* clear the filer table, reject any packet by default */
+	rqfcr = RQFCR_RJE | RQFCR_CMP_MATCH;
+	for (i = 0; i <= MAX_FILER_IDX; i++)
+		gfar_write_filer(priv, i, rqfcr, 0);
+
+	i = 0;
+	if (priv->wol_opts & GFAR_WOL_FILER_UCAST) {
+		/* unicast packet, accept it */
+		struct net_device *ndev = priv->ndev;
+		/* get the default rx queue index */
+		u8 qindex = (u8)priv->gfargrp[0].rx_queue->qindex;
+		u32 dest_mac_addr = (ndev->dev_addr[0] << 16) |
+				    (ndev->dev_addr[1] << 8) |
+				     ndev->dev_addr[2];
+
+		rqfcr = (qindex << 10) | RQFCR_AND |
+			RQFCR_CMP_EXACT | RQFCR_PID_DAH;
+
+		gfar_write_filer(priv, i++, rqfcr, dest_mac_addr);
+
+		dest_mac_addr = (ndev->dev_addr[3] << 16) |
+				(ndev->dev_addr[4] << 8) |
+				 ndev->dev_addr[5];
+		rqfcr = (qindex << 10) | RQFCR_GPI |
+			RQFCR_CMP_EXACT | RQFCR_PID_DAL;
+		gfar_write_filer(priv, i++, rqfcr, dest_mac_addr);
+	}
+
+	__gfar_filer_enable(priv);
+}
+
+static void gfar_filer_restore_table(struct gfar_private *priv)
+{
+	u32 rqfcr, rqfpr;
+	unsigned int i;
+
+	__gfar_filer_disable(priv);
+
+	for (i = 0; i <= MAX_FILER_IDX; i++) {
+		rqfcr = priv->ftp_rqfcr[i];
+		rqfpr = priv->ftp_rqfpr[i];
+		gfar_write_filer(priv, i, rqfcr, rqfpr);
+	}
+
+	__gfar_filer_enable(priv);
+}
+
+/* gfar_start() for Rx only and with the FGPI filer interrupt enabled */
+static void gfar_start_wol_filer(struct gfar_private *priv)
+{
+	struct gfar __iomem *regs = priv->gfargrp[0].regs;
+	u32 tempval;
+	int i = 0;
+
+	/* Enable Rx hw queues */
+	gfar_write(&regs->rqueue, priv->rqueue);
+
+	/* Initialize DMACTRL to have WWR and WOP */
+	tempval = gfar_read(&regs->dmactrl);
+	tempval |= DMACTRL_INIT_SETTINGS;
+	gfar_write(&regs->dmactrl, tempval);
+
+	/* Make sure we aren't stopped */
+	tempval = gfar_read(&regs->dmactrl);
+	tempval &= ~DMACTRL_GRS;
+	gfar_write(&regs->dmactrl, tempval);
+
+	for (i = 0; i < priv->num_grps; i++) {
+		regs = priv->gfargrp[i].regs;
+		/* Clear RHLT, so that the DMA starts polling now */
+		gfar_write(&regs->rstat, priv->gfargrp[i].rstat);
+		/* enable the Filer General Purpose Interrupt */
+		gfar_write(&regs->imask, IMASK_FGPI);
+	}
+
+	/* Enable Rx DMA */
+	tempval = gfar_read(&regs->maccfg1);
+	tempval |= MACCFG1_RX_EN;
+	gfar_write(&regs->maccfg1, tempval);
+}
+
+static int gfar_suspend(struct device *dev)
+{
+	struct gfar_private *priv = dev_get_drvdata(dev);
+	struct net_device *ndev = priv->ndev;
+	struct gfar __iomem *regs = priv->gfargrp[0].regs;
+	u32 tempval;
+	u16 wol = priv->wol_opts;
+
+	if (!netif_running(ndev))
+		return 0;
+
+	disable_napi(priv);
+	netif_tx_lock(ndev);
+	netif_device_detach(ndev);
+	netif_tx_unlock(ndev);
+
+	gfar_halt(priv);
+
+	if (wol & GFAR_WOL_MAGIC) {
+		/* Enable interrupt on Magic Packet */
+		gfar_write(&regs->imask, IMASK_MAG);
+
+		/* Enable Magic Packet mode */
+		tempval = gfar_read(&regs->maccfg2);
+		tempval |= MACCFG2_MPEN;
+		gfar_write(&regs->maccfg2, tempval);
+
+		/* re-enable the Rx block */
+		tempval = gfar_read(&regs->maccfg1);
+		tempval |= MACCFG1_RX_EN;
+		gfar_write(&regs->maccfg1, tempval);
+
+	} else if (wol & GFAR_WOL_FILER_UCAST) {
+		gfar_filer_config_wol(priv);
+		gfar_start_wol_filer(priv);
+
+	} else {
+		phy_stop(ndev->phydev);
+	}
+
+	return 0;
+}
+
+static int gfar_resume(struct device *dev)
+{
+	struct gfar_private *priv = dev_get_drvdata(dev);
+	struct net_device *ndev = priv->ndev;
+	struct gfar __iomem *regs = priv->gfargrp[0].regs;
+	u32 tempval;
+	u16 wol = priv->wol_opts;
+
+	if (!netif_running(ndev))
+		return 0;
+
+	if (wol & GFAR_WOL_MAGIC) {
+		/* Disable Magic Packet mode */
+		tempval = gfar_read(&regs->maccfg2);
+		tempval &= ~MACCFG2_MPEN;
+		gfar_write(&regs->maccfg2, tempval);
+
+	} else if (wol & GFAR_WOL_FILER_UCAST) {
+		/* need to stop rx only, tx is already down */
+		gfar_halt(priv);
+		gfar_filer_restore_table(priv);
+
+	} else {
+		phy_start(ndev->phydev);
+	}
+
+	gfar_start(priv);
+
+	netif_device_attach(ndev);
+	enable_napi(priv);
+
+	return 0;
+}
+
+static int gfar_restore(struct device *dev)
+{
+	struct gfar_private *priv = dev_get_drvdata(dev);
+	struct net_device *ndev = priv->ndev;
+
+	if (!netif_running(ndev)) {
+		netif_device_attach(ndev);
+
+		return 0;
+	}
+
+	gfar_init_bds(ndev);
+
+	gfar_mac_reset(priv);
+
+	gfar_init_tx_rx_base(priv);
+
+	gfar_start(priv);
+
+	priv->oldlink = 0;
+	priv->oldspeed = 0;
+	priv->oldduplex = -1;
+
+	if (ndev->phydev)
+		phy_start(ndev->phydev);
+
+	netif_device_attach(ndev);
+	enable_napi(priv);
+
+	return 0;
+}
+
+static const struct dev_pm_ops gfar_pm_ops = {
+	.suspend = gfar_suspend,
+	.resume = gfar_resume,
+	.freeze = gfar_suspend,
+	.thaw = gfar_resume,
+	.restore = gfar_restore,
+};
+
+#define GFAR_PM_OPS (&gfar_pm_ops)
+
+#else
+
+#define GFAR_PM_OPS NULL
+
+#endif
+
 static const struct of_device_id gfar_match[] =
 {
 	{
diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h
index f2af96349c7b..f058594a67fb 100644
--- a/drivers/net/ethernet/freescale/gianfar.h
+++ b/drivers/net/ethernet/freescale/gianfar.h
@@ -1326,16 +1326,9 @@ static inline u32 gfar_rxbd_dma_lastfree(struct gfar_priv_rx_q *rxq)
 	return bdp_dma;
 }
 
-irqreturn_t gfar_receive(int irq, void *dev_id);
 int startup_gfar(struct net_device *dev);
 void stop_gfar(struct net_device *dev);
-void reset_gfar(struct net_device *dev);
 void gfar_mac_reset(struct gfar_private *priv);
-void gfar_halt(struct gfar_private *priv);
-void gfar_start(struct gfar_private *priv);
-void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable,
-		   u32 regnum, u32 read);
-void gfar_configure_coalescing_all(struct gfar_private *priv);
 int gfar_set_features(struct net_device *dev, netdev_features_t features);
 
 extern const struct ethtool_ops gfar_ethtool_ops;
diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
index 3433b46b90c1..3c8e4e2efc07 100644
--- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
+++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
@@ -45,19 +45,6 @@
 
 #define GFAR_MAX_COAL_USECS 0xffff
 #define GFAR_MAX_COAL_FRAMES 0xff
-static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
-			    u64 *buf);
-static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf);
-static int gfar_gcoalesce(struct net_device *dev,
-			  struct ethtool_coalesce *cvals);
-static int gfar_scoalesce(struct net_device *dev,
-			  struct ethtool_coalesce *cvals);
-static void gfar_gringparam(struct net_device *dev,
-			    struct ethtool_ringparam *rvals);
-static int gfar_sringparam(struct net_device *dev,
-			   struct ethtool_ringparam *rvals);
-static void gfar_gdrvinfo(struct net_device *dev,
-			  struct ethtool_drvinfo *drvinfo);
 
 static const char stat_gstrings[][ETH_GSTRING_LEN] = {
 	/* extra stats */
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 2/4] gianfar: make five functions static
  2019-09-04 13:52         ` [PATCH 0/4] gianfar: some assorted cleanup Arseny Solokha
  2019-09-04 13:52           ` [PATCH 1/4] gianfar: remove forward declarations Arseny Solokha
@ 2019-09-04 13:52           ` Arseny Solokha
  2019-09-04 13:52           ` [PATCH 3/4] gianfar: cleanup gianfar.h Arseny Solokha
                             ` (3 subsequent siblings)
  5 siblings, 0 replies; 26+ messages in thread
From: Arseny Solokha @ 2019-09-04 13:52 UTC (permalink / raw)
  To: Claudiu Manoil, Ioana Ciornei, Russell King, Andrew Lunn,
	Vladimir Oltean, Florian Fainelli
  Cc: netdev, Arseny Solokha

Make functions that do not have callers outside the translation unit they
are defined in static.

Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru>
---
 drivers/net/ethernet/freescale/gianfar.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index fc31ba1a8bb8..17fb412e4bb4 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -275,7 +275,7 @@ static void gfar_configure_coalescing(struct gfar_private *priv,
 	}
 }
 
-void gfar_configure_coalescing_all(struct gfar_private *priv)
+static void gfar_configure_coalescing_all(struct gfar_private *priv)
 {
 	gfar_configure_coalescing(priv, 0xFF, 0xFF);
 }
@@ -1063,7 +1063,7 @@ static void gfar_halt_nodisable(struct gfar_private *priv)
 }
 
 /* Halt the receive and transmit queues */
-void gfar_halt(struct gfar_private *priv)
+static void gfar_halt(struct gfar_private *priv)
 {
 	struct gfar __iomem *regs = priv->gfargrp[0].regs;
 	u32 tempval;
@@ -1194,7 +1194,7 @@ void stop_gfar(struct net_device *dev)
 	free_skb_resources(priv);
 }
 
-void gfar_start(struct gfar_private *priv)
+static void gfar_start(struct gfar_private *priv)
 {
 	struct gfar __iomem *regs = priv->gfargrp[0].regs;
 	u32 tempval;
@@ -2324,7 +2324,7 @@ static void count_errors(u32 lstatus, struct net_device *ndev)
 	}
 }
 
-irqreturn_t gfar_receive(int irq, void *grp_id)
+static irqreturn_t gfar_receive(int irq, void *grp_id)
 {
 	struct gfar_priv_grp *grp = (struct gfar_priv_grp *)grp_id;
 	unsigned long flags;
@@ -2526,7 +2526,8 @@ static void gfar_process_frame(struct net_device *ndev, struct sk_buff *skb)
  * until the budget/quota has been reached. Returns the number
  * of frames handled
  */
-int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
+static int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue,
+			      int rx_work_limit)
 {
 	struct net_device *ndev = rx_queue->ndev;
 	struct gfar_private *priv = netdev_priv(ndev);
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 3/4] gianfar: cleanup gianfar.h
  2019-09-04 13:52         ` [PATCH 0/4] gianfar: some assorted cleanup Arseny Solokha
  2019-09-04 13:52           ` [PATCH 1/4] gianfar: remove forward declarations Arseny Solokha
  2019-09-04 13:52           ` [PATCH 2/4] gianfar: make five functions static Arseny Solokha
@ 2019-09-04 13:52           ` Arseny Solokha
  2019-09-04 13:52           ` [PATCH 4/4] gianfar: use DT more consistently when selecting PHY connection type Arseny Solokha
                             ` (2 subsequent siblings)
  5 siblings, 0 replies; 26+ messages in thread
From: Arseny Solokha @ 2019-09-04 13:52 UTC (permalink / raw)
  To: Claudiu Manoil, Ioana Ciornei, Russell King, Andrew Lunn,
	Vladimir Oltean, Florian Fainelli
  Cc: netdev, Arseny Solokha

Remove now unused macro and structure definitions from gianfar.h that have
accumulated there over time.

Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru>
---
 drivers/net/ethernet/freescale/gianfar.h | 38 ------------------------
 1 file changed, 38 deletions(-)

diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h
index f058594a67fb..f472a6dbbe6f 100644
--- a/drivers/net/ethernet/freescale/gianfar.h
+++ b/drivers/net/ethernet/freescale/gianfar.h
@@ -67,8 +67,6 @@ struct ethtool_rx_list {
 /* Number of bytes to align the rx bufs to */
 #define RXBUF_ALIGNMENT 64
 
-#define PHY_INIT_TIMEOUT 100000
-
 #define DRV_NAME "gfar-enet"
 extern const char gfar_driver_version[];
 
@@ -88,10 +86,6 @@ extern const char gfar_driver_version[];
 #define GFAR_RX_MAX_RING_SIZE   256
 #define GFAR_TX_MAX_RING_SIZE   256
 
-#define GFAR_MAX_FIFO_THRESHOLD 511
-#define GFAR_MAX_FIFO_STARVE	511
-#define GFAR_MAX_FIFO_STARVE_OFF 511
-
 #define FBTHR_SHIFT        24
 #define DEFAULT_RX_LFC_THR  16
 #define DEFAULT_LFC_PTVVAL  4
@@ -109,9 +103,6 @@ extern const char gfar_driver_version[];
 #define DEFAULT_FIFO_TX_THR 0x100
 #define DEFAULT_FIFO_TX_STARVE 0x40
 #define DEFAULT_FIFO_TX_STARVE_OFF 0x80
-#define DEFAULT_BD_STASH 1
-#define DEFAULT_STASH_LENGTH	96
-#define DEFAULT_STASH_INDEX	0
 
 /* The number of Exact Match registers */
 #define GFAR_EM_NUM	15
@@ -139,15 +130,6 @@ extern const char gfar_driver_version[];
 #define DEFAULT_RX_COALESCE 0
 #define DEFAULT_RXCOUNT	0
 
-#define GFAR_SUPPORTED (SUPPORTED_10baseT_Half \
-		| SUPPORTED_10baseT_Full \
-		| SUPPORTED_100baseT_Half \
-		| SUPPORTED_100baseT_Full \
-		| SUPPORTED_Autoneg \
-		| SUPPORTED_MII)
-
-#define GFAR_SUPPORTED_GBIT SUPPORTED_1000baseT_Full
-
 /* TBI register addresses */
 #define MII_TBICON		0x11
 
@@ -185,8 +167,6 @@ extern const char gfar_driver_version[];
 #define ECNTRL_REDUCED_MII_MODE	0x00000004
 #define ECNTRL_SGMII_MODE	0x00000002
 
-#define MRBLR_INIT_SETTINGS	DEFAULT_RX_BUFFER_SIZE
-
 #define MINFLR_INIT_SETTINGS	0x00000040
 
 /* Tqueue control */
@@ -266,12 +246,6 @@ extern const char gfar_driver_version[];
 #define DEFAULT_TXIC mk_ic_value(DEFAULT_TXCOUNT, DEFAULT_TXTIME)
 #define DEFAULT_RXIC mk_ic_value(DEFAULT_RXCOUNT, DEFAULT_RXTIME)
 
-#define skip_bd(bdp, stride, base, ring_size) ({ \
-	typeof(bdp) new_bd = (bdp) + (stride); \
-	(new_bd >= (base) + (ring_size)) ? (new_bd - (ring_size)) : new_bd; })
-
-#define next_bd(bdp, base, ring_size) skip_bd(bdp, 1, base, ring_size)
-
 #define RCTRL_TS_ENABLE 	0x01000000
 #define RCTRL_PAL_MASK		0x001f0000
 #define RCTRL_LFC		0x00004000
@@ -385,11 +359,6 @@ extern const char gfar_driver_version[];
 #define IMASK_RX_DISABLED ((~(IMASK_RX_DEFAULT)) & IMASK_DEFAULT)
 #define IMASK_TX_DISABLED ((~(IMASK_TX_DEFAULT)) & IMASK_DEFAULT)
 
-/* Fifo management */
-#define FIFO_TX_THR_MASK	0x01ff
-#define FIFO_TX_STARVE_MASK	0x01ff
-#define FIFO_TX_STARVE_OFF_MASK	0x01ff
-
 /* Attribute fields */
 
 /* This enables rx snooping for buffers and descriptors */
@@ -1341,13 +1310,6 @@ extern const struct ethtool_ops gfar_ethtool_ops;
 #define RQFCR_PID_PORT_MASK 0xFFFF0000
 #define RQFCR_PID_MAC_MASK 0xFF000000
 
-struct gfar_mask_entry {
-	unsigned int mask; /* The mask value which is valid form start to end */
-	unsigned int start;
-	unsigned int end;
-	unsigned int block; /* Same block values indicate depended entries */
-};
-
 /* Represents a receive filer table entry */
 struct gfar_filer_entry {
 	u32 ctrl;
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 4/4] gianfar: use DT more consistently when selecting PHY connection type
  2019-09-04 13:52         ` [PATCH 0/4] gianfar: some assorted cleanup Arseny Solokha
                             ` (2 preceding siblings ...)
  2019-09-04 13:52           ` [PATCH 3/4] gianfar: cleanup gianfar.h Arseny Solokha
@ 2019-09-04 13:52           ` Arseny Solokha
  2019-09-05 10:28           ` [PATCH 0/4] gianfar: some assorted cleanup David Miller
  2019-09-05 10:39           ` Vladimir Oltean
  5 siblings, 0 replies; 26+ messages in thread
From: Arseny Solokha @ 2019-09-04 13:52 UTC (permalink / raw)
  To: Claudiu Manoil, Ioana Ciornei, Russell King, Andrew Lunn,
	Vladimir Oltean, Florian Fainelli
  Cc: netdev, Arseny Solokha

Historically, gianfar only used phy-connection-type DT property when
connected to PHY in the rgmii-id mode. It ignored the property otherwise,
relying on the connection type auto-detection carried out by MAC and
providing that reconstructed mode to of_phy_connect(). It also did not
consider alternative phy-mode property at all.

Make the driver properly query DT node for PHY connection type first and
use an obtained value if it was specified there. Otherwise, if a particular
DT relies on connection type auto-detection, fall back to reconstructing
the value from MAC registers, as before.

Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru>
---
 drivers/net/ethernet/freescale/gianfar.c | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 17fb412e4bb4..24bf7f68375f 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -639,7 +639,6 @@ static phy_interface_t gfar_get_interface(struct net_device *dev)
 static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
 {
 	const char *model;
-	const char *ctype;
 	const void *mac_addr;
 	int err = 0, i;
 	struct net_device *dev = NULL;
@@ -802,13 +801,15 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
 				     FSL_GIANFAR_DEV_HAS_TIMER |
 				     FSL_GIANFAR_DEV_HAS_RX_FILER;
 
-	err = of_property_read_string(np, "phy-connection-type", &ctype);
-
-	/* We only care about rgmii-id.  The rest are autodetected */
-	if (err == 0 && !strcmp(ctype, "rgmii-id"))
-		priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
+	/* Use PHY connection type from the DT node if one is specified there.
+	 * rgmii-id really needs to be specified. Other types can be
+	 * detected by hardware
+	 */
+	err = of_get_phy_mode(np);
+	if (err >= 0)
+		priv->interface = err;
 	else
-		priv->interface = PHY_INTERFACE_MODE_MII;
+		priv->interface = gfar_get_interface(dev);
 
 	if (of_find_property(np, "fsl,magic-packet", NULL))
 		priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
@@ -1670,7 +1671,7 @@ static int init_phy(struct net_device *dev)
 {
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
 	struct gfar_private *priv = netdev_priv(dev);
-	phy_interface_t interface;
+	phy_interface_t interface = priv->interface;
 	struct phy_device *phydev;
 	struct ethtool_eee edata;
 
@@ -1686,8 +1687,6 @@ static int init_phy(struct net_device *dev)
 	priv->oldspeed = 0;
 	priv->oldduplex = -1;
 
-	interface = gfar_get_interface(dev);
-
 	phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0,
 				interface);
 	if (!phydev) {
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/2] gianfar: convert to phylink
  2019-07-29 23:39   ` Vladimir Oltean
  2019-07-30 10:23     ` Russell King - ARM Linux admin
  2019-07-30 14:40     ` Arseny Solokha
@ 2019-09-04 13:53     ` Arseny Solokha
  2 siblings, 0 replies; 26+ messages in thread
From: Arseny Solokha @ 2019-09-04 13:53 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Claudiu Manoil, Russell King, Ioana Ciornei, Andrew Lunn, netdev,
	Florian Fainelli

Hi,


>> @@ -1964,7 +1966,7 @@ void stop_gfar(struct net_device *dev)
>>         /* disable ints and gracefully shut down Rx/Tx DMA */
>>         gfar_halt(priv);
>>
>> -       phy_stop(dev->phydev);
>> +       phylink_stop(priv->phylink);
>>
>>         free_skb_resources(priv);
>>  }
>> @@ -2219,12 +2221,7 @@ int startup_gfar(struct net_device *ndev)
>>         /* Start Rx/Tx DMA and enable the interrupts */
>>         gfar_start(priv);
>>
>> -       /* force link state update after mac reset */
>> -       priv->oldlink = 0;
>> -       priv->oldspeed = 0;
>> -       priv->oldduplex = -1;
>> -
>> -       phy_start(ndev->phydev);
>> +       phylink_start(priv->phylink);
>>
>>         enable_napi(priv);
>>
>> @@ -2593,7 +2590,7 @@ static int gfar_close(struct net_device *dev)
>>         stop_gfar(dev);
>>
>>         /* Disconnect from the PHY */
>> -       phy_disconnect(dev->phydev);
>> +       phylink_disconnect_phy(priv->phylink);
>
> It is very odd to disconnect from the PHY on ndo_close and connect
> back on ndo_open. I don't know of any other driver that does that.
> Can't you change the behavior to simply start and stop phylink here?

I surely can. But I've just looked at xilinx_axienet, mvneta, mvpp2, and stmmac,
and they all call phylink_stop() and phylink_disconnect_phy() in ndo_stop. What
do you think would justify such a change?

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC PATCH 1/2] gianfar: convert to phylink
  2019-08-24 15:21       ` Vladimir Oltean
  2019-08-28 15:20         ` Vladimir Oltean
  2019-09-04 13:52         ` [PATCH 0/4] gianfar: some assorted cleanup Arseny Solokha
@ 2019-09-04 13:54         ` Arseny Solokha
  2 siblings, 0 replies; 26+ messages in thread
From: Arseny Solokha @ 2019-09-04 13:54 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: Claudiu Manoil, Russell King, Ioana Ciornei, Andrew Lunn, netdev,
	Florian Fainelli

> Hi Arseny.
>
> On Tue, 30 Jul 2019 at 17:40, Arseny Solokha <asolokha@kb.kras.ru> wrote:
>>
>> > Hi Arseny,
>> >
>> > Nice project!
>>
>> Vladimir, Russell, thanks for your review. I'm on vacation now, so won't fully
>> address your comments in a few weeks: while I can build the code, I won't have
>> access to hardware to test.
>>
>> So it seems this patch will turn into a series where we'll have some cleanup
>> patches preceding the actual conversion (and the latter will also contain a
>> documentation change in Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
>> which I've overlooked in the first submission). I'll try to post trivial
>> cleanups independently while still on vacation.
>>
>
> Yes, ideally the cleanup would be separate from the conversion.

I've just sent a cleanup series. It won't make the conversion easier to digest,
though.


>> >> @@ -3387,23 +3384,6 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id)
>> >>         return IRQ_HANDLED;
>> >>  }
>> >>
>> >> -/* Called every time the controller might need to be made
>> >> - * aware of new link state.  The PHY code conveys this
>> >> - * information through variables in the phydev structure, and this
>> >> - * function converts those variables into the appropriate
>> >> - * register values, and can bring down the device if needed.
>> >> - */
>> >> -static void adjust_link(struct net_device *dev)
>> >> -{
>> >> -       struct gfar_private *priv = netdev_priv(dev);
>> >> -       struct phy_device *phydev = dev->phydev;
>> >> -
>> >> -       if (unlikely(phydev->link != priv->oldlink ||
>> >> -                    (phydev->link && (phydev->duplex != priv->oldduplex ||
>> >> -                                      phydev->speed != priv->oldspeed))))
>> >> -               gfar_update_link_state(priv);
>> >> -}
>> >
>> > Getting rid of the cruft from this function deserves its own patch.
>>
>> How am I supposed to remove it without breaking the PHYLIB-based driver? Or do
>> you mean making it call gfar_update_link_state() just before the conversion
>> which will then remove adjust_link() altogether?
>>
>
> I don't know, if you can't refactor without breaking anything then ok.

I can, of course, effectively revert 6ce29b0e2a04 ("gianfar: Avoid unnecessary
reg accesses in adjust_link()") and 2a4eebf0c485 ("gianfar: Restore link state
settings after MAC reset") just before the conversion, but I fail to see a point
in that. It would only make subsequent bisection harder.


>> >>         if (unlikely(test_bit(GFAR_RESETTING, &priv->state)))
>> >>                 return;
>> >>
>> >> -       if (phydev->link) {
>> >> -               u32 tempval1 = gfar_read(&regs->maccfg1);
>> >> -               u32 tempval = gfar_read(&regs->maccfg2);
>> >> -               u32 ecntrl = gfar_read(&regs->ecntrl);
>> >> -               u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW);
>> >> +       if (unlikely(phylink_autoneg_inband(mode)))
>> >> +               return;
>> >>
>> >> -               if (phydev->duplex != priv->oldduplex) {
>> >> -                       if (!(phydev->duplex))
>> >> -                               tempval &= ~(MACCFG2_FULL_DUPLEX);
>> >> -                       else
>> >> -                               tempval |= MACCFG2_FULL_DUPLEX;
>> >> +       maccfg1 = gfar_read(&regs->maccfg1);
>> >> +       maccfg2 = gfar_read(&regs->maccfg2);
>> >> +       ecntrl = gfar_read(&regs->ecntrl);
>> >>
>> >> -                       priv->oldduplex = phydev->duplex;
>> >> -               }
>> >> +       new_maccfg2 = maccfg2 & ~(MACCFG2_FULL_DUPLEX | MACCFG2_IF);
>> >> +       new_ecntrl = ecntrl & ~ECNTRL_R100;
>> >>
>> >> -               if (phydev->speed != priv->oldspeed) {
>> >> -                       switch (phydev->speed) {
>> >> -                       case 1000:
>> >> -                               tempval =
>> >> -                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
>> >> +       if (state->duplex)
>> >> +               new_maccfg2 |= MACCFG2_FULL_DUPLEX;
>> >>
>> >> -                               ecntrl &= ~(ECNTRL_R100);
>> >> -                               break;
>> >> -                       case 100:
>> >> -                       case 10:
>> >> -                               tempval =
>> >> -                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
>> >> -
>> >> -                               /* Reduced mode distinguishes
>> >> -                                * between 10 and 100
>> >> -                                */
>> >> -                               if (phydev->speed == SPEED_100)
>> >> -                                       ecntrl |= ECNTRL_R100;
>> >> -                               else
>> >> -                                       ecntrl &= ~(ECNTRL_R100);
>> >> -                               break;
>> >> -                       default:
>> >> -                               netif_warn(priv, link, priv->ndev,
>> >> -                                          "Ack!  Speed (%d) is not 10/100/1000!\n",
>> >> -                                          phydev->speed);
>> >
>> > Please 1. remove "Ack!" 2. treat SPEED_UNKNOWN here by setting the MAC
>> > into a low-power state (e.g. 10 Mbps - the power savings are real).
>> > Don't print that Speed -1 is not 10/100/1000, we know that.
>>
>> In my first conversion attempt I see "Ack!" when changing link speed on when
>> shutting it down, so switching to 10 Mbps doesn't seem right for me—hence early
>> return in this case. Maybe I'm doing something wrong here.
>>
>
> When mac_config calls with SPEED_UNKNOWN, the link is down, and you
> can put the MAC in the lowest energy state it can go to (10 Mbps, in
> this case). Or so I've been told. Maybe Russell can chime in. Anyway,
> you don't need to print anything, there's lots of prints from PHYLINK
> already.

OK. I believe this comment only applies to a PHYLINK-based driver and I should
omit this change from a cleanup series? Because in the PHYLIB-based driver this
code only gets executed when the link is up, and I can't immediately see how it
could be called with phydev->speed set to SPEED_UNKNOWN.


>> >> diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
>> >> index 3433b46b90c1..146b30d07789 100644
>> >> --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
>> >> +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
>> >> @@ -35,7 +35,7 @@
>> >>  #include <asm/types.h>
>> >>  #include <linux/ethtool.h>
>> >>  #include <linux/mii.h>
>> >> -#include <linux/phy.h>
>> >> +#include <linux/phylink.h>
>> >>  #include <linux/sort.h>
>> >>  #include <linux/if_vlan.h>
>> >>  #include <linux/of_platform.h>
>> >> @@ -207,12 +207,10 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs,
>> >>  static unsigned int gfar_usecs2ticks(struct gfar_private *priv,
>> >>                                      unsigned int usecs)
>> >>  {
>> >> -       struct net_device *ndev = priv->ndev;
>> >> -       struct phy_device *phydev = ndev->phydev;
>> >
>> > Are you sure this still works? You missed a ndev->phydev check from
>> > gfar_gcoalesce, where this is called from. Technically you can still
>> > check ndev->phydev, it's just that PHYLINK doesn't guarantee you'll
>> > have one (e.g. fixed-link interfaces).
>>
>> It still works for RGMII PHYs, SGMII and 1000Base-X in my testing. I didn't
>> check it with fixed-link, though.
>>
>>
>> >> @@ -1519,6 +1472,24 @@ static int gfar_get_ts_info(struct net_device *dev,
>> >>         return 0;
>> >>  }
>> >>
>> >> +/* Set link ksettings (phy address, speed) for ethtools */
>> >
>> > ethtool, not ethtools. Also, I'm not quite sure what you mean by
>> > setting the "phy address" with ethtool.
>>
>> Well, I know where I've copied it from… Thanks for pointing it out.
>
> Regards,
> -Vladimir

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 0/4] gianfar: some assorted cleanup
  2019-09-04 13:52         ` [PATCH 0/4] gianfar: some assorted cleanup Arseny Solokha
                             ` (3 preceding siblings ...)
  2019-09-04 13:52           ` [PATCH 4/4] gianfar: use DT more consistently when selecting PHY connection type Arseny Solokha
@ 2019-09-05 10:28           ` David Miller
  2019-09-05 10:39           ` Vladimir Oltean
  5 siblings, 0 replies; 26+ messages in thread
From: David Miller @ 2019-09-05 10:28 UTC (permalink / raw)
  To: asolokha
  Cc: claudiu.manoil, ioana.ciornei, linux, andrew, olteanv,
	f.fainelli, netdev

From: Arseny Solokha <asolokha@kb.kras.ru>
Date: Wed,  4 Sep 2019 20:52:18 +0700

> This is a cleanup series for the gianfar Ethernet driver, following up a
> discussion in [1]. It is intended to precede a conversion of gianfar from
> PHYLIB to PHYLINK API, which will be submitted later in its version 2.
> However, it won't make a conversion cleaner, except for the last patch in
> this series. Obviously this series is not intended for -stable.
> 
> The first patch looks super controversial to me, as it moves lots of code
> around for the sole purpose of getting rid of static forward declarations
> in two translation units. On the other hand, this change is purely
> mechanical and cannot do any harm other than cluttering git blame output.
> I can prepare an alternative patch for only swapping adjacent functions
> around, if necessary.
> 
> The second patch is a trivial follow-up to the first one, making functions
> that are only called from the same translation unit static.
> 
> The third patch removes some now unused macro and structure definitions
> from gianfar.h, slipped away from various cleanups in the past.
> 
> The fourth patch, also suggested in [1], makes the driver consistently use
> PHY connection type value obtained from a Device Tree node, instead of
> ignoring it and using the one auto-detected by MAC, when connecting to PHY.
> Obviously a value has to be specified correctly in DT source, or omitted
> altogether, in which case the driver will fall back to auto-detection. When
> querying a DT node, the driver will also take both applicable properties
> into account by making a proper API call instead of open-coding the lookup
> half-way correctly.
> 
> [1] https://lore.kernel.org/netdev/CA+h21hruqt6nGG5ksDSwrGH_w5GtGF4fjAMCWJne7QJrjusERQ@mail.gmail.com/

Series applied, thanks.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 0/4] gianfar: some assorted cleanup
  2019-09-04 13:52         ` [PATCH 0/4] gianfar: some assorted cleanup Arseny Solokha
                             ` (4 preceding siblings ...)
  2019-09-05 10:28           ` [PATCH 0/4] gianfar: some assorted cleanup David Miller
@ 2019-09-05 10:39           ` Vladimir Oltean
  5 siblings, 0 replies; 26+ messages in thread
From: Vladimir Oltean @ 2019-09-05 10:39 UTC (permalink / raw)
  To: Arseny Solokha
  Cc: Claudiu Manoil, Ioana Ciornei, Russell King, Andrew Lunn,
	Florian Fainelli, netdev

On Wed, 4 Sep 2019 at 16:53, Arseny Solokha <asolokha@kb.kras.ru> wrote:
>
> This is a cleanup series for the gianfar Ethernet driver, following up a
> discussion in [1]. It is intended to precede a conversion of gianfar from
> PHYLIB to PHYLINK API, which will be submitted later in its version 2.
> However, it won't make a conversion cleaner, except for the last patch in
> this series. Obviously this series is not intended for -stable.
>
> The first patch looks super controversial to me, as it moves lots of code
> around for the sole purpose of getting rid of static forward declarations
> in two translation units. On the other hand, this change is purely
> mechanical and cannot do any harm other than cluttering git blame output.
> I can prepare an alternative patch for only swapping adjacent functions
> around, if necessary.
>
> The second patch is a trivial follow-up to the first one, making functions
> that are only called from the same translation unit static.
>
> The third patch removes some now unused macro and structure definitions
> from gianfar.h, slipped away from various cleanups in the past.
>
> The fourth patch, also suggested in [1], makes the driver consistently use
> PHY connection type value obtained from a Device Tree node, instead of
> ignoring it and using the one auto-detected by MAC, when connecting to PHY.
> Obviously a value has to be specified correctly in DT source, or omitted
> altogether, in which case the driver will fall back to auto-detection. When
> querying a DT node, the driver will also take both applicable properties
> into account by making a proper API call instead of open-coding the lookup
> half-way correctly.
>
> [1] https://lore.kernel.org/netdev/CA+h21hruqt6nGG5ksDSwrGH_w5GtGF4fjAMCWJne7QJrjusERQ@mail.gmail.com/
>
> Arseny Solokha (4):
>   gianfar: remove forward declarations
>   gianfar: make five functions static
>   gianfar: cleanup gianfar.h
>   gianfar: use DT more consistently when selecting PHY connection type
>
>  drivers/net/ethernet/freescale/gianfar.c      | 4647 ++++++++---------
>  drivers/net/ethernet/freescale/gianfar.h      |   45 -
>  .../net/ethernet/freescale/gianfar_ethtool.c  |   13 -
>  3 files changed, 2303 insertions(+), 2402 deletions(-)
>
> --
> 2.23.0
>

Thanks for the cleanup!

-Vladimir

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2019-09-05 10:39 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-23 15:17 [RFC PATCH 0/2] convert gianfar to phylink Arseny Solokha
2019-07-23 15:17 ` [RFC PATCH 1/2] gianfar: convert " Arseny Solokha
2019-07-23 16:07   ` Claudiu Manoil
2019-07-24  7:36     ` Arseny Solokha
2019-07-24  8:19   ` Russell King - ARM Linux admin
2019-07-29 23:39   ` Vladimir Oltean
2019-07-30 10:23     ` Russell King - ARM Linux admin
2019-08-24 12:49       ` Vladimir Oltean
2019-07-30 14:40     ` Arseny Solokha
2019-08-24 15:21       ` Vladimir Oltean
2019-08-28 15:20         ` Vladimir Oltean
2019-09-04 13:52         ` [PATCH 0/4] gianfar: some assorted cleanup Arseny Solokha
2019-09-04 13:52           ` [PATCH 1/4] gianfar: remove forward declarations Arseny Solokha
2019-09-04 13:52           ` [PATCH 2/4] gianfar: make five functions static Arseny Solokha
2019-09-04 13:52           ` [PATCH 3/4] gianfar: cleanup gianfar.h Arseny Solokha
2019-09-04 13:52           ` [PATCH 4/4] gianfar: use DT more consistently when selecting PHY connection type Arseny Solokha
2019-09-05 10:28           ` [PATCH 0/4] gianfar: some assorted cleanup David Miller
2019-09-05 10:39           ` Vladimir Oltean
2019-09-04 13:54         ` [RFC PATCH 1/2] gianfar: convert to phylink Arseny Solokha
2019-09-04 13:53     ` Arseny Solokha
2019-07-23 15:17 ` [RFC PATCH 2/2] net: phylink: don't start and stop SGMII PHYs in SFP modules twice Arseny Solokha
2019-07-24  9:01   ` Russell King - ARM Linux admin
2019-07-24 13:31     ` [PATCH v2] " Arseny Solokha
2019-07-24 13:36       ` Andrew Lunn
2019-07-24 13:37       ` Russell King - ARM Linux admin
2019-07-24 21:38       ` David Miller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.