From 483991c363ddf316b758ce87ef7d837a6eeeceea Mon Sep 17 00:00:00 2001
From: matteo <matteo@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Date: Tue, 17 May 2011 11:12:56 +0000
Subject: [PATCH] ar71xx: detect link on LAN ports

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@26922 3c298f89-4303-0410-b956-a3cf2f4a3e73
---
 .../ar71xx/files/drivers/net/ag71xx/ag71xx.h  |  1 +
 .../files/drivers/net/ag71xx/ag71xx_ar7240.c  | 30 ++++++++++++++++++-
 .../files/drivers/net/ag71xx/ag71xx_phy.c     |  4 +--
 3 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h
index 342d12186..30caaff02 100644
--- a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h
+++ b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx.h
@@ -162,6 +162,7 @@ struct ag71xx {
 	int			duplex;
 
 	struct work_struct	restart_work;
+	struct delayed_work	link_work;
 	struct timer_list	oom_timer;
 
 #ifdef CONFIG_AG71XX_DEBUG_FS
diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_ar7240.c b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_ar7240.c
index 1bc7f58c1..d84cc81ae 100644
--- a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_ar7240.c
+++ b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_ar7240.c
@@ -828,6 +828,30 @@ static struct ar7240sw *ar7240_probe(struct ag71xx *ag)
 	return as;
 }
 
+static void link_function(struct work_struct *work) {
+	struct ag71xx *ag = container_of(work, struct ag71xx, link_work.work);
+	unsigned long flags;
+	int i;
+	int status = 0;
+
+	for (i = 0; i < 4; i++) {
+		int link = ar7240sw_phy_read(ag->mii_bus, i, MII_BMSR);
+		if(link & BMSR_LSTATUS) {
+			status = 1;
+			break;
+		}
+	}
+
+	spin_lock_irqsave(&ag->lock, flags);
+	if(status != ag->link) {
+		ag->link = status;
+		ag71xx_link_adjust(ag);
+	}
+	spin_unlock_irqrestore(&ag->lock, flags);
+
+	schedule_delayed_work(&ag->link_work, HZ / 2);
+}
+
 void ag71xx_ar7240_start(struct ag71xx *ag)
 {
 	struct ar7240sw *as = ag->phy_priv;
@@ -836,15 +860,17 @@ void ag71xx_ar7240_start(struct ag71xx *ag)
 	ar7240sw_setup(as);
 
 	ag->speed = SPEED_1000;
-	ag->link = 1;
 	ag->duplex = 1;
 
 	ar7240_set_addr(as, ag->dev->dev_addr);
 	ar7240_hw_apply(&as->swdev);
+
+	schedule_delayed_work(&ag->link_work, HZ / 10);
 }
 
 void ag71xx_ar7240_stop(struct ag71xx *ag)
 {
+	cancel_delayed_work_sync(&ag->link_work);
 }
 
 int __devinit ag71xx_ar7240_init(struct ag71xx *ag)
@@ -858,6 +884,8 @@ int __devinit ag71xx_ar7240_init(struct ag71xx *ag)
 	ag->phy_priv = as;
 	ar7240sw_reset(as);
 
+	INIT_DELAYED_WORK(&ag->link_work, link_function);
+
 	return 0;
 }
 
diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c
index d0bae03b1..75500277f 100644
--- a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c
+++ b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c
@@ -48,9 +48,9 @@ void ag71xx_phy_start(struct ag71xx *ag)
 
 	if (ag->phy_dev) {
 		phy_start(ag->phy_dev);
+	} else if (pdata->has_ar7240_switch) {
+		ag71xx_ar7240_start(ag);
 	} else {
-		if (pdata->has_ar7240_switch)
-			ag71xx_ar7240_start(ag);
 		ag->link = 1;
 		ag71xx_link_adjust(ag);
 	}
-- 
2.20.1