netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Stephen Hemminger <shemminger@osdl.org>
To: Jeff Garzik <jgarzik@pobox.com>
Cc: netdev@vger.kernel.org
Subject: [PATCH 08/10] sky2: flow control setting fixes
Date: Wed, 11 Oct 2006 14:22:33 -0700	[thread overview]
Message-ID: <20061011212311.365293806@osdl.org> (raw)
In-Reply-To: 20061011212225.941046083@osdl.org

[-- Attachment #1: sky2-fc-auto.patch --]
[-- Type: text/plain, Size: 7351 bytes --]

The result of flow control negotiation should not limit the next
negotiatition. If board is plugged into an old half duplex 10Mbit port,
without pause, then replugged into a gigabit port, it should negotiate
what is desired, not inherit that last negotiation.

P.s: many other drivers have this bug.

Signed-off-by: Stephen Hemminger <shemminger@osdl.org>

---
 drivers/net/sky2.c |  132 ++++++++++++++++++++++++++++++-----------------------
 drivers/net/sky2.h |   13 ++++-
 2 files changed, 86 insertions(+), 59 deletions(-)

--- sky2.orig/drivers/net/sky2.c	2006-10-11 12:00:37.000000000 -0700
+++ sky2/drivers/net/sky2.c	2006-10-11 12:05:46.000000000 -0700
@@ -284,6 +284,31 @@
 	gma_write16(hw, port, GM_RX_CTRL, reg);
 }
 
+/* flow control to advertise bits */
+static const u16 copper_fc_adv[] = {
+	[FC_NONE]	= 0,
+	[FC_TX]		= PHY_M_AN_ASP,
+	[FC_RX]		= PHY_M_AN_PC,
+	[FC_BOTH]	= PHY_M_AN_PC | PHY_M_AN_ASP,
+};
+
+/* flow control to advertise bits when using 1000BaseX */
+static const u16 fiber_fc_adv[] = {
+	[FC_BOTH] = PHY_M_P_BOTH_MD_X,
+	[FC_TX]   = PHY_M_P_ASYM_MD_X,
+	[FC_RX]	  = PHY_M_P_SYM_MD_X,
+	[FC_NONE] = PHY_M_P_NO_PAUSE_X,
+};
+
+/* flow control to GMA disable bits */
+static const u16 gm_fc_disable[] = {
+	[FC_NONE] = GM_GPCR_FC_RX_DIS | GM_GPCR_FC_TX_DIS,
+	[FC_TX]	  = GM_GPCR_FC_RX_DIS,
+	[FC_RX]	  = GM_GPCR_FC_TX_DIS,
+	[FC_BOTH] = 0,
+};
+
+
 static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
 {
 	struct sky2_port *sky2 = netdev_priv(hw->dev[port]);
@@ -376,29 +401,14 @@
 			if (sky2->advertising & ADVERTISED_10baseT_Half)
 				adv |= PHY_M_AN_10_HD;
 
-			/* desired flow control */
-			if (sky2->tx_pause && sky2->rx_pause)	/* both */
-				adv |= PHY_M_AN_PC | PHY_M_AN_ASP;
-			else if (sky2->tx_pause)
-				adv |= PHY_M_AN_ASP;
-			else if (sky2->rx_pause)
-				adv |= PHY_M_AN_PC;
-
-
+			adv |= copper_fc_adv[sky2->flow_mode];
 		} else {	/* special defines for FIBER (88E1040S only) */
 			if (sky2->advertising & ADVERTISED_1000baseT_Full)
 				adv |= PHY_M_AN_1000X_AFD;
 			if (sky2->advertising & ADVERTISED_1000baseT_Half)
 				adv |= PHY_M_AN_1000X_AHD;
 
-			if (sky2->tx_pause && sky2->rx_pause)	/* both */
- 				adv |= PHY_M_P_BOTH_MD_X;
-			else if (sky2->tx_pause)
-				adv |= PHY_M_P_ASYM_MD_X;
-			else if (sky2->rx_pause)
- 				adv |= PHY_M_P_SYM_MD_X;
-			else
-				adv |= PHY_M_P_NO_PAUSE_X;
+			adv |= fiber_fc_adv[sky2->flow_mode];
 		}
 
 		/* Restart Auto-negotiation */
@@ -424,20 +434,14 @@
 		if (sky2->duplex == DUPLEX_FULL) {
 			reg |= GM_GPCR_DUP_FULL;
 			ctrl |= PHY_CT_DUP_MD;
-		} else if (sky2->speed != SPEED_1000 && hw->chip_id != CHIP_ID_YUKON_EC_U) {
-			/* Turn off flow control for 10/100mbps */
-			sky2->rx_pause = 0;
-			sky2->tx_pause = 0;
-		}
+		} else if (sky2->speed < SPEED_1000)
+			sky2->flow_mode = FC_NONE;
 
-		if (!sky2->rx_pause)
-			reg |= GM_GPCR_FC_RX_DIS;
 
-		if (!sky2->tx_pause)
-			reg |= GM_GPCR_FC_TX_DIS;
+ 		reg |= gm_fc_disable[sky2->flow_mode];
 
 		/* Forward pause packets to GMAC? */
-		if (sky2->tx_pause || sky2->rx_pause)
+		if (sky2->flow_mode & FC_RX)
 			sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON);
 		else
 			sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
@@ -1605,6 +1609,12 @@
 	struct sky2_hw *hw = sky2->hw;
 	unsigned port = sky2->port;
 	u16 reg;
+	static const char *fc_name[] = {
+		[FC_NONE]	= "none",
+		[FC_TX]		= "tx",
+		[FC_RX]		= "rx",
+		[FC_BOTH]	= "both",
+	};
 
 	/* enable Rx/Tx */
 	reg = gma_read16(hw, port, GM_GP_CTRL);
@@ -1648,8 +1658,7 @@
 		       "%s: Link is up at %d Mbps, %s duplex, flow control %s\n",
 		       sky2->netdev->name, sky2->speed,
 		       sky2->duplex == DUPLEX_FULL ? "full" : "half",
-		       (sky2->tx_pause && sky2->rx_pause) ? "both" :
-		       sky2->tx_pause ? "tx" : sky2->rx_pause ? "rx" : "none");
+		       fc_name[sky2->flow_status]);
 }
 
 static void sky2_link_down(struct sky2_port *sky2)
@@ -1664,7 +1673,7 @@
 	reg &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
 	gma_write16(hw, port, GM_GP_CTRL, reg);
 
-	if (sky2->rx_pause && !sky2->tx_pause) {
+	if (sky2->flow_status == FC_RX) {
 		/* restore Asymmetric Pause bit */
 		gm_phy_write(hw, port, PHY_MARV_AUNE_ADV,
 			     gm_phy_read(hw, port, PHY_MARV_AUNE_ADV)
@@ -1683,6 +1692,14 @@
 	sky2_phy_init(hw, port);
 }
 
+static enum flow_control sky2_flow(int rx, int tx)
+{
+	if (rx)
+		return tx ? FC_BOTH : FC_RX;
+	else
+		return tx ? FC_TX : FC_NONE;
+}
+
 static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux)
 {
 	struct sky2_hw *hw = sky2->hw;
@@ -1709,14 +1726,14 @@
 	if (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U)
 		aux >>= 6;
 
-	sky2->rx_pause = (aux & PHY_M_PS_RX_P_EN) != 0;
-	sky2->tx_pause = (aux & PHY_M_PS_TX_P_EN) != 0;
+	sky2->flow_status = sky2_flow(aux & PHY_M_PS_RX_P_EN,
+				      aux & PHY_M_PS_TX_P_EN);
 
-	if (sky2->duplex == DUPLEX_HALF && sky2->speed != SPEED_1000
+	if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000
 	    && hw->chip_id != CHIP_ID_YUKON_EC_U)
-		sky2->rx_pause = sky2->tx_pause = 0;
+		sky2->flow_status = FC_NONE;
 
-	if (sky2->rx_pause || sky2->tx_pause)
+	if (aux & PHY_M_PS_RX_P_EN)
 		sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON);
 	else
 		sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
@@ -2729,7 +2746,7 @@
 {
 	struct sky2_port *sky2 = netdev_priv(dev);
 
-	if (sky2->autoneg != AUTONEG_ENABLE)
+	if (!netif_running(dev) || sky2->autoneg != AUTONEG_ENABLE)
 		return -EINVAL;
 
 	sky2_phy_reinit(sky2);
@@ -2971,8 +2988,20 @@
 {
 	struct sky2_port *sky2 = netdev_priv(dev);
 
-	ecmd->tx_pause = sky2->tx_pause;
-	ecmd->rx_pause = sky2->rx_pause;
+	switch (sky2->flow_mode) {
+	case FC_NONE:
+		ecmd->tx_pause = ecmd->rx_pause = 0;
+		break;
+	case FC_TX:
+		ecmd->tx_pause = 1, ecmd->rx_pause = 0;
+		break;
+	case FC_RX:
+		ecmd->tx_pause = 0, ecmd->rx_pause = 1;
+		break;
+	case FC_BOTH:
+		ecmd->tx_pause = ecmd->rx_pause = 1;
+	}
+
 	ecmd->autoneg = sky2->autoneg;
 }
 
@@ -2982,10 +3011,10 @@
 	struct sky2_port *sky2 = netdev_priv(dev);
 
 	sky2->autoneg = ecmd->autoneg;
-	sky2->tx_pause = ecmd->tx_pause != 0;
-	sky2->rx_pause = ecmd->rx_pause != 0;
+	sky2->flow_mode = sky2_flow(ecmd->rx_pause, ecmd->tx_pause);
 
-	sky2_phy_reinit(sky2);
+	if (netif_running(dev))
+		sky2_phy_reinit(sky2);
 
 	return 0;
 }
@@ -3215,8 +3244,8 @@
 
 	/* Auto speed and flow control */
 	sky2->autoneg = AUTONEG_ENABLE;
-	sky2->tx_pause = 1;
-	sky2->rx_pause = 1;
+	sky2->flow_mode = FC_BOTH;
+
 	sky2->duplex = -1;
 	sky2->speed = -1;
 	sky2->advertising = sky2_supported_modes(hw);
--- sky2.orig/drivers/net/sky2.h	2006-10-11 11:59:32.000000000 -0700
+++ sky2/drivers/net/sky2.h	2006-10-11 12:01:20.000000000 -0700
@@ -1828,6 +1828,13 @@
 	dma_addr_t	frag_addr[ETH_JUMBO_MTU >> PAGE_SHIFT];
 };
 
+enum flow_control {
+	FC_NONE	= 0,
+	FC_TX	= 1,
+	FC_RX	= 2,
+	FC_BOTH	= 3,
+};
+
 struct sky2_port {
 	struct sky2_hw	     *hw;
 	struct net_device    *netdev;
@@ -1864,9 +1871,9 @@
 	u16		     speed;	/* SPEED_1000, SPEED_100, ... */
 	u8		     autoneg;	/* AUTONEG_ENABLE, AUTONEG_DISABLE */
 	u8		     duplex;	/* DUPLEX_HALF, DUPLEX_FULL */
-	u8		     rx_pause;
-	u8		     tx_pause;
 	u8		     rx_csum;
+ 	enum flow_control    flow_mode;
+ 	enum flow_control    flow_status;
 
 	struct net_device_stats net_stats;
 

--
Stephen Hemminger <shemminger@osdl.org>


  parent reply	other threads:[~2006-10-11 21:29 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-10-11 21:22 [PATCH 00/10] sky2: version 1.9 patches Stephen Hemminger
2006-10-11 21:22 ` [PATCH 01/10] sky2: MSI test is only a warning Stephen Hemminger
2006-10-11 21:22 ` [PATCH 02/10] sky2: turn of workaround timer Stephen Hemminger
2006-10-11 21:22 ` [PATCH 03/10] sky2: phy irq on shutdown Stephen Hemminger
2006-10-11 21:22 ` [PATCH 04/10] sky2: fiber pause bits Stephen Hemminger
2006-10-11 21:22 ` [PATCH 05/10] sky2: advertising register 16 bits Stephen Hemminger
2006-10-11 21:22 ` [PATCH 06/10] sky2: use duplex result bits Stephen Hemminger
2006-10-11 21:22 ` [PATCH 07/10] sky2: dont reset PHY twice Stephen Hemminger
2006-10-11 21:22 ` Stephen Hemminger [this message]
2006-10-11 21:22 ` [PATCH 09/10] sky2: no message on rx fifo overflow Stephen Hemminger
2006-10-11 21:22 ` [PATCH 10/10] sky2: version 1.9 Stephen Hemminger
2006-10-16 20:50   ` [PATCH 1/2] sky2: multicast pause frame receive Stephen Hemminger
2006-10-16 20:52   ` [PATCH 2/2] sky2: don't process pause frames in recveiver Stephen Hemminger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20061011212311.365293806@osdl.org \
    --to=shemminger@osdl.org \
    --cc=jgarzik@pobox.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).