sis190: fix cable detect via link status poll
authorHerton Ronaldo Krzesinski <herton@mandriva.com.br>
Tue, 2 Mar 2010 11:44:41 +0000 (03:44 -0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 2 Mar 2010 11:44:41 +0000 (03:44 -0800)
Some sis190 devices don't report LinkChange, so do polling for
link status.

Fixes http://bugzilla.kernel.org/show_bug.cgi?id=11926

Signed-off-by: Herton Ronaldo Krzesinski <herton@mandriva.com.br>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/sis190.c

index 80af4a4..760d9e8 100644 (file)
@@ -285,6 +285,11 @@ struct sis190_private {
        struct list_head first_phy;
        u32 features;
        u32 negotiated_lpa;
+       enum {
+               LNK_OFF,
+               LNK_ON,
+               LNK_AUTONEG,
+       } link_status;
 };
 
 struct sis190_phy {
@@ -750,6 +755,7 @@ static irqreturn_t sis190_interrupt(int irq, void *__dev)
 
        if (status & LinkChange) {
                netif_info(tp, intr, dev, "link change\n");
+               del_timer(&tp->timer);
                schedule_work(&tp->phy_task);
        }
 
@@ -922,12 +928,15 @@ static void sis190_phy_task(struct work_struct *work)
        if (val & BMCR_RESET) {
                // FIXME: needlessly high ?  -- FR 02/07/2005
                mod_timer(&tp->timer, jiffies + HZ/10);
-       } else if (!(mdio_read_latched(ioaddr, phy_id, MII_BMSR) &
-                    BMSR_ANEGCOMPLETE)) {
+               goto out_unlock;
+       }
+
+       val = mdio_read_latched(ioaddr, phy_id, MII_BMSR);
+       if (!(val & BMSR_ANEGCOMPLETE) && tp->link_status != LNK_AUTONEG) {
                netif_carrier_off(dev);
                netif_warn(tp, link, dev, "auto-negotiating...\n");
-               mod_timer(&tp->timer, jiffies + SIS190_PHY_TIMEOUT);
-       } else {
+               tp->link_status = LNK_AUTONEG;
+       } else if ((val & BMSR_LSTATUS) && tp->link_status != LNK_ON) {
                /* Rejoice ! */
                struct {
                        int val;
@@ -1000,7 +1009,10 @@ static void sis190_phy_task(struct work_struct *work)
 
                netif_info(tp, link, dev, "link on %s mode\n", p->msg);
                netif_carrier_on(dev);
-       }
+               tp->link_status = LNK_ON;
+       } else if (!(val & BMSR_LSTATUS) && tp->link_status != LNK_AUTONEG)
+               tp->link_status = LNK_OFF;
+       mod_timer(&tp->timer, jiffies + SIS190_PHY_TIMEOUT);
 
 out_unlock:
        rtnl_unlock();
@@ -1513,6 +1525,7 @@ static struct net_device * __devinit sis190_init_board(struct pci_dev *pdev)
 
        tp->pci_dev = pdev;
        tp->mmio_addr = ioaddr;
+       tp->link_status = LNK_OFF;
 
        sis190_irq_mask_and_ack(ioaddr);