From a7154cb8326c6015b6c5d9ce482d2a1f4c268d99 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Fran=C3=A7ois=20Romieu?= Date: Wed, 2 Jun 2004 12:03:02 -0400 Subject: [PATCH] [PATCH] r8169: link handling and phy reset rework Link handling changes (Andy Lutomirski ): - removed rtl8169_hw_phy_reset() and its busy loop; - RTL8169_PHY_TIMEOUT is x10 to account for the removal of the phy_link_down_cnt loop in rtl8169_phy_timer(); - added spinlocking in timer context for rtl8169_phy_timer to avoid messing with the {set/get}_settings commands issued via ethtool; - more TBI stuff. This patch differs from the former version on the following points: - the LinkChg irq does not enable the phy timer when the link goes down any more; - the phy timer is not enabled in rtl8169_set_speed(); - removal of the initial renegotiation hack. --- drivers/net/r8169.c | 142 +++++++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 49 deletions(-) diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 70cba257e8df..f1612343235d 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -41,6 +41,7 @@ VERSION 1.2 <2002/11/30> #include #include #include +#include #include #include #include @@ -107,7 +108,7 @@ static int multicast_filter_limit = 32; #define RTL_MIN_IO_SIZE 0x80 #define RTL8169_TX_TIMEOUT (6*HZ) -#define RTL8169_PHY_TIMEOUT (HZ) +#define RTL8169_PHY_TIMEOUT (10*HZ) /* write/read MMIO register */ #define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) @@ -341,7 +342,6 @@ struct rtl8169_private { struct sk_buff *Rx_skbuff[NUM_RX_DESC]; /* Rx data buffers */ struct sk_buff *Tx_skbuff[NUM_TX_DESC]; /* Tx data buffers */ struct timer_list timer; - unsigned long phy_link_down_cnt; u16 cp_cmd; u16 intr_mask; int phy_auto_nego_reg; @@ -349,6 +349,9 @@ struct rtl8169_private { int (*set_speed)(struct net_device *, u8 autoneg, u16 speed, u8 duplex); void (*get_settings)(struct net_device *, struct ethtool_cmd *); + void (*phy_reset_enable)(void *); + unsigned int (*phy_reset_pending)(void *); + unsigned int (*link_ok)(void *); }; MODULE_AUTHOR("Realtek"); @@ -374,7 +377,7 @@ static int rtl8169_poll(struct net_device *dev, int *budget); static const u16 rtl8169_intr_mask = LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; static const u16 rtl8169_napi_event = - RxOK | LinkChg | RxOverflow | RxFIFOOver | TxOK | TxErr; + RxOK | RxOverflow | RxFIFOOver | TxOK | TxErr; static const unsigned int rtl8169_rx_config = (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); @@ -416,6 +419,53 @@ static int mdio_read(void *ioaddr, int RegAddr) return value; } +static unsigned int rtl8169_tbi_reset_pending(void *ioaddr) +{ + return RTL_R32(TBICSR) & TBIReset; +} + +static unsigned int rtl8169_xmii_reset_pending(void *ioaddr) +{ + return mdio_read(ioaddr, 0) & 0x8000; +} + +static unsigned int rtl8169_tbi_link_ok(void *ioaddr) +{ + return RTL_R32(TBICSR) & TBILinkOk; +} + +static unsigned int rtl8169_xmii_link_ok(void *ioaddr) +{ + return RTL_R8(PHYstatus) & LinkStatus; +} + +static void rtl8169_tbi_reset_enable(void *ioaddr) +{ + RTL_W32(TBICSR, RTL_R32(TBICSR) | TBIReset); +} + +static void rtl8169_xmii_reset_enable(void *ioaddr) +{ + unsigned int val; + + val = (mdio_read(ioaddr, PHY_CTRL_REG) | 0x8000) & 0xffff; + mdio_write(ioaddr, PHY_CTRL_REG, val); +} + +static void rtl8169_check_link_status(struct net_device *dev, + struct rtl8169_private *tp, void *ioaddr) +{ + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + if (tp->link_ok(ioaddr)) { + netif_carrier_on(dev); + printk(KERN_INFO PFX "%s: link up\n", dev->name); + } else + netif_carrier_off(dev); + spin_unlock_irqrestore(&tp->lock, flags); +} + static void rtl8169_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { @@ -493,8 +543,14 @@ static int rtl8169_set_speed(struct net_device *dev, u8 autoneg, u16 speed, u8 duplex) { struct rtl8169_private *tp = netdev_priv(dev); + int ret; - return tp->set_speed(dev, autoneg, speed, duplex); + ret = tp->set_speed(dev, autoneg, speed, duplex); + + if (tp->phy_1000_ctrl_reg & PHY_Cap_1000_Full) + mod_timer(&tp->timer, jiffies + RTL8169_PHY_TIMEOUT); + + return ret; } static int rtl8169_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) @@ -753,56 +809,42 @@ static void rtl8169_hw_phy_config(struct net_device *dev) mdio_write(ioaddr, 31, 0x0000); //w 31 2 0 0 } -static void rtl8169_hw_phy_reset(struct net_device *dev) -{ - struct rtl8169_private *tp = netdev_priv(dev); - void *ioaddr = tp->mmio_addr; - int i, val; - - printk(KERN_WARNING PFX "%s: Reset RTL8169s PHY\n", dev->name); - - val = (mdio_read(ioaddr, 0) | 0x8000) & 0xffff; - mdio_write(ioaddr, 0, val); - - for (i = 50; i >= 0; i--) { - if (!(mdio_read(ioaddr, 0) & 0x8000)) - break; - udelay(100); /* Gross */ - } - - if (i < 0) { - printk(KERN_WARNING PFX "%s: no PHY Reset ack. Giving up.\n", - dev->name); - } -} - static void rtl8169_phy_timer(unsigned long __opaque) { struct net_device *dev = (struct net_device *)__opaque; struct rtl8169_private *tp = netdev_priv(dev); struct timer_list *timer = &tp->timer; void *ioaddr = tp->mmio_addr; + unsigned long timeout = RTL8169_PHY_TIMEOUT; assert(tp->mac_version > RTL_GIGA_MAC_VER_B); assert(tp->phy_version < RTL_GIGA_PHY_VER_G); - if (RTL_R8(PHYstatus) & LinkStatus) - tp->phy_link_down_cnt = 0; - else { - tp->phy_link_down_cnt++; - if (tp->phy_link_down_cnt >= 12) { - int reg; + if (!(tp->phy_1000_ctrl_reg & PHY_Cap_1000_Full)) + return; - // If link on 1000, perform phy reset. - reg = mdio_read(ioaddr, PHY_1000_CTRL_REG); - if (reg & PHY_Cap_1000_Full) - rtl8169_hw_phy_reset(dev); + spin_lock_irq(&tp->lock); - tp->phy_link_down_cnt = 0; - } + if (tp->phy_reset_pending(ioaddr)) { + /* + * A busy loop could burn quite a few cycles on nowadays CPU. + * Let's delay the execution of the timer for a few ticks. + */ + timeout = HZ/10; + goto out_mod_timer; } - mod_timer(timer, jiffies + RTL8169_PHY_TIMEOUT); + if (tp->link_ok(ioaddr)) + goto out_unlock; + + printk(KERN_WARNING PFX "%s: PHY reset until link up\n", dev->name); + + tp->phy_reset_enable(ioaddr); + +out_mod_timer: + mod_timer(timer, jiffies + timeout); +out_unlock: + spin_unlock_irq(&tp->lock); } static inline void rtl8169_delete_timer(struct net_device *dev) @@ -815,8 +857,6 @@ static inline void rtl8169_delete_timer(struct net_device *dev) return; del_timer_sync(timer); - - tp->phy_link_down_cnt = 0; } static inline void rtl8169_request_timer(struct net_device *dev) @@ -828,8 +868,6 @@ static inline void rtl8169_request_timer(struct net_device *dev) (tp->phy_version >= RTL_GIGA_PHY_VER_G)) return; - tp->phy_link_down_cnt = 0; - init_timer(timer); timer->expires = jiffies + RTL8169_PHY_TIMEOUT; timer->data = (unsigned long)(dev); @@ -1014,11 +1052,17 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (RTL_R8(PHYstatus) & TBI_Enable) { tp->set_speed = rtl8169_set_speed_tbi; tp->get_settings = rtl8169_gset_tbi; + tp->phy_reset_enable = rtl8169_tbi_reset_enable; + tp->phy_reset_pending = rtl8169_tbi_reset_pending; + tp->link_ok = rtl8169_tbi_link_ok; tp->phy_1000_ctrl_reg = PHY_Cap_1000_Full; /* Implied by TBI */ } else { tp->set_speed = rtl8169_set_speed_xmii; tp->get_settings = rtl8169_gset_xmii; + tp->phy_reset_enable = rtl8169_xmii_reset_enable; + tp->phy_reset_pending = rtl8169_xmii_reset_pending; + tp->link_ok = rtl8169_xmii_link_ok; } // Get MAC address. FIXME: read EEPROM @@ -1752,10 +1796,7 @@ rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs) break; handled = 1; -/* - if (status & LinkChg) - link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; -*/ + status &= tp->intr_mask; RTL_W16(IntrStatus, (status & RxFIFOOver) ? (status | RxOverflow) : status); @@ -1763,6 +1804,9 @@ rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs) if (!(status & rtl8169_intr_mask)) break; + if (status & LinkChg) + rtl8169_check_link_status(dev, tp, ioaddr); + #ifdef CONFIG_R8169_NAPI RTL_W16(IntrMask, rtl8169_intr_mask & ~rtl8169_napi_event); tp->intr_mask = ~rtl8169_napi_event; @@ -1776,7 +1820,7 @@ rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs) break; #else // Rx interrupt - if (status & (RxOK | LinkChg | RxOverflow | RxFIFOOver)) { + if (status & (RxOK | RxOverflow | RxFIFOOver)) { rtl8169_rx_interrupt(dev, tp, ioaddr); } // Tx interrupt -- 2.47.3