[source] ath25: 4.9: fix Ethernet link autonegotiation

LEDE Commits lede-commits at lists.infradead.org
Wed Jun 7 03:25:35 PDT 2017


blogic pushed a commit to source.git, branch master:
https://git.lede-project.org/3e3d482c98024aadd94200acfdf811c43b5cd7c4

commit 3e3d482c98024aadd94200acfdf811c43b5cd7c4
Author: Sergey Ryazanov <ryazanov.s.a at gmail.com>
AuthorDate: Wed Jun 7 01:49:38 2017 +0300

    ath25: 4.9: fix Ethernet link autonegotiation
    
    Drop the own PHY polling function and switch to using the kernel PHY
    state machine. This change allows driver to work correctly with devices
    that do not support PHY behaviour but whose driver could emulate
    autonegotiation completion (e.g. MV88E6060 and IP17xx switches).
    
    NB: earlier this driver rely on flaws in PHY core code and could use PHY
    device without really starting it. But now (at least in kernel 4.9)
    this trick no more work and network interface could stuck in not-running
    state.
    
    Signed-off-by: Sergey Ryazanov <ryazanov.s.a at gmail.com>
---
 .../ath25/patches-4.9/110-ar2313_ethernet.patch    | 142 ++++-----------------
 .../patches-4.9/220-enet_micrel_workaround.patch   |  24 +++-
 2 files changed, 50 insertions(+), 116 deletions(-)

diff --git a/target/linux/ath25/patches-4.9/110-ar2313_ethernet.patch b/target/linux/ath25/patches-4.9/110-ar2313_ethernet.patch
index ed3dcb4..39638a2 100644
--- a/target/linux/ath25/patches-4.9/110-ar2313_ethernet.patch
+++ b/target/linux/ath25/patches-4.9/110-ar2313_ethernet.patch
@@ -33,7 +33,7 @@
 +obj-$(CONFIG_NET_AR231X) += ar231x.o
 --- /dev/null
 +++ b/drivers/net/ethernet/atheros/ar231x/ar231x.c
-@@ -0,0 +1,1198 @@
+@@ -0,0 +1,1119 @@
 +/*
 + * ar231x.c: Linux driver for the Atheros AR231x Ethernet device.
 + *
@@ -318,9 +318,6 @@
 +		return -ENODEV;
 +	}
 +
-+	/* start link poll timer */
-+	ar231x_setup_timer(dev);
-+
 +	return 0;
 +}
 +
@@ -498,93 +495,6 @@
 +	}
 +}
 +
-+static int ar231x_setup_timer(struct net_device *dev)
-+{
-+	struct ar231x_private *sp = netdev_priv(dev);
-+
-+	init_timer(&sp->link_timer);
-+
-+	sp->link_timer.function = ar231x_link_timer_fn;
-+	sp->link_timer.data = (int)dev;
-+	sp->link_timer.expires = jiffies + HZ;
-+
-+	add_timer(&sp->link_timer);
-+	return 0;
-+}
-+
-+static void ar231x_link_timer_fn(unsigned long data)
-+{
-+	struct net_device *dev = (struct net_device *)data;
-+	struct ar231x_private *sp = netdev_priv(dev);
-+
-+	/**
-+	 * See if the link status changed.
-+	 * This was needed to make sure we set the PHY to the
-+	 * autonegotiated value of half or full duplex.
-+	 */
-+	ar231x_check_link(dev);
-+
-+	/**
-+	 * Loop faster when we don't have link.
-+	 * This was needed to speed up the AP bootstrap time.
-+	 */
-+	if (sp->link == 0)
-+		mod_timer(&sp->link_timer, jiffies + HZ / 2);
-+	else
-+		mod_timer(&sp->link_timer, jiffies + LINK_TIMER);
-+}
-+
-+static void ar231x_check_link(struct net_device *dev)
-+{
-+	struct ar231x_private *sp = netdev_priv(dev);
-+	u16 phy_data;
-+
-+	phy_data = ar231x_mdiobus_read(sp->mii_bus, sp->phy, MII_BMSR);
-+	if (sp->phy_data != phy_data) {
-+		if (phy_data & BMSR_LSTATUS) {
-+			/**
-+			 * Link is present, ready link partner ability to
-+			 * deterine duplexity.
-+			 */
-+			int duplex = 0;
-+			u16 reg;
-+
-+			sp->link = 1;
-+			reg = ar231x_mdiobus_read(sp->mii_bus, sp->phy,
-+						  MII_BMCR);
-+			if (reg & BMCR_ANENABLE) {
-+				/* auto neg enabled */
-+				reg = ar231x_mdiobus_read(sp->mii_bus, sp->phy,
-+							  MII_LPA);
-+				duplex = reg & (LPA_100FULL | LPA_10FULL) ?
-+					 1 : 0;
-+			} else {
-+				/* no auto neg, just read duplex config */
-+				duplex = (reg & BMCR_FULLDPLX) ? 1 : 0;
-+			}
-+
-+			printk(KERN_INFO "%s: Configuring MAC for %s duplex\n",
-+			       dev->name, (duplex) ? "full" : "half");
-+
-+			if (duplex) {
-+				/* full duplex */
-+				sp->eth_regs->mac_control =
-+					(sp->eth_regs->mac_control |
-+					 MAC_CONTROL_F) & ~MAC_CONTROL_DRO;
-+			} else {
-+				/* half duplex */
-+				sp->eth_regs->mac_control =
-+					(sp->eth_regs->mac_control |
-+					 MAC_CONTROL_DRO) & ~MAC_CONTROL_F;
-+			}
-+		} else {
-+			/* no link */
-+			sp->link = 0;
-+		}
-+		sp->phy_data = phy_data;
-+	}
-+}
-+
 +static int ar231x_reset_reg(struct net_device *dev)
 +{
 +	struct ar231x_private *sp = netdev_priv(dev);
@@ -996,6 +906,8 @@
 +
 +	sp->eth_regs->mac_control |= MAC_CONTROL_RE;
 +
++	phy_start(sp->phy_dev);
++
 +	return 0;
 +}
 +
@@ -1055,6 +967,7 @@
 + */
 +static int ar231x_close(struct net_device *dev)
 +{
++	struct ar231x_private *sp = netdev_priv(dev);
 +#if 0
 +	/* Disable interrupts */
 +	disable_irq(dev->irq);
@@ -1073,6 +986,9 @@
 +	free_irq(dev->irq, dev);
 +
 +#endif
++
++	phy_stop(sp->phy_dev);
++
 +	return 0;
 +}
 +
@@ -1131,21 +1047,28 @@
 +static void ar231x_adjust_link(struct net_device *dev)
 +{
 +	struct ar231x_private *sp = netdev_priv(dev);
-+	unsigned int mc;
++	struct phy_device *phydev = sp->phy_dev;
++	u32 mc;
 +
-+	if (!sp->phy_dev->link)
++	if (!phydev->link) {
++		if (sp->link) {
++			pr_info("%s: link down\n", dev->name);
++			sp->link = 0;
++		}
 +		return;
-+
-+	if (sp->phy_dev->duplex != sp->oldduplex) {
-+		mc = readl(&sp->eth_regs->mac_control);
-+		mc &= ~(MAC_CONTROL_F | MAC_CONTROL_DRO);
-+		if (sp->phy_dev->duplex)
-+			mc |= MAC_CONTROL_F;
-+		else
-+			mc |= MAC_CONTROL_DRO;
-+		writel(mc, &sp->eth_regs->mac_control);
-+		sp->oldduplex = sp->phy_dev->duplex;
 +	}
++	sp->link = 1;
++
++	pr_info("%s: link up (%uMbps/%s duplex)\n", dev->name,
++		phydev->speed, phydev->duplex ? "full" : "half");
++
++	mc = sp->eth_regs->mac_control;
++	if (phydev->duplex)
++		mc = (mc | MAC_CONTROL_F) & ~MAC_CONTROL_DRO;
++	else
++		mc = (mc | MAC_CONTROL_DRO) & ~MAC_CONTROL_F;
++	sp->eth_regs->mac_control = mc;
++	sp->duplex = phydev->duplex;
 +}
 +
 +#define MII_ADDR(phy, reg) \
@@ -1222,9 +1145,7 @@
 +
 +	phydev->advertising = phydev->supported;
 +
-+	sp->oldduplex = -1;
 +	sp->phy_dev = phydev;
-+	sp->phy = phydev->mdio.addr;
 +
 +	printk(KERN_INFO "%s: attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
 +	       dev->name, phydev->drv->name, phydev_name(phydev));
@@ -1234,7 +1155,7 @@
 +
 --- /dev/null
 +++ b/drivers/net/ethernet/atheros/ar231x/ar231x.h
-@@ -0,0 +1,288 @@
+@@ -0,0 +1,281 @@
 +/*
 + * ar231x.h: Linux driver for the Atheros AR231x Ethernet device.
 + *
@@ -1491,18 +1412,14 @@
 +		char *mapping;
 +	} desc;
 +
-+	struct timer_list link_timer;
-+	unsigned short phy;		/* merlot phy = 1, samsung phy = 0x1f */
-+	unsigned short mac;
 +	unsigned short link;		/* 0 - link down, 1 - link up */
-+	u16 phy_data;
++	unsigned short duplex;		/* 0 - half, 1 - full */
 +
 +	struct tasklet_struct rx_tasklet;
 +	int unloading;
 +
 +	struct phy_device *phy_dev;
 +	struct mii_bus *mii_bus;
-+	int oldduplex;
 +};
 +
 +/* Prototypes */
@@ -1518,9 +1435,6 @@
 +static int ar231x_close(struct net_device *dev);
 +static int ar231x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
 +static void ar231x_init_cleanup(struct net_device *dev);
-+static int ar231x_setup_timer(struct net_device *dev);
-+static void ar231x_link_timer_fn(unsigned long data);
-+static void ar231x_check_link(struct net_device *dev);
 +
 +#endif	/* _AR2313_H_ */
 --- a/arch/mips/ath25/ar2315_regs.h
diff --git a/target/linux/ath25/patches-4.9/220-enet_micrel_workaround.patch b/target/linux/ath25/patches-4.9/220-enet_micrel_workaround.patch
index 398495a..91b9792 100644
--- a/target/linux/ath25/patches-4.9/220-enet_micrel_workaround.patch
+++ b/target/linux/ath25/patches-4.9/220-enet_micrel_workaround.patch
@@ -66,7 +66,7 @@
  	if (ar231x_mdiobus_probe(dev) != 0) {
  		printk(KERN_ERR "%s: mdiobus_probe failed\n", dev->name);
  		rx_tasklet_cleanup(dev);
-@@ -329,8 +374,10 @@ static int ar231x_remove(struct platform
+@@ -326,8 +371,10 @@ static int ar231x_remove(struct platform
  	rx_tasklet_cleanup(dev);
  	ar231x_init_cleanup(dev);
  	unregister_netdev(dev);
@@ -79,7 +79,27 @@
  	kfree(dev);
  	return 0;
  }
-@@ -1079,6 +1126,9 @@ static int ar231x_ioctl(struct net_devic
+@@ -870,7 +917,8 @@ static int ar231x_open(struct net_device
+ 
+ 	sp->eth_regs->mac_control |= MAC_CONTROL_RE;
+ 
+-	phy_start(sp->phy_dev);
++	if (sp->phy_dev)
++		phy_start(sp->phy_dev);
+ 
+ 	return 0;
+ }
+@@ -951,7 +999,8 @@ static int ar231x_close(struct net_devic
+ 
+ #endif
+ 
+-	phy_stop(sp->phy_dev);
++	if (sp->phy_dev)
++		phy_stop(sp->phy_dev);
+ 
+ 	return 0;
+ }
+@@ -995,6 +1044,9 @@ static int ar231x_ioctl(struct net_devic
  {
  	struct ar231x_private *sp = netdev_priv(dev);
  



More information about the lede-commits mailing list