Commit a7154cb8 authored by François Romieu's avatar François Romieu Committed by Jeff Garzik

[PATCH] r8169: link handling and phy reset rework

Link handling changes (Andy Lutomirski <luto@myrealbox.com>):
- 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.
parent c74c29fa
...@@ -41,6 +41,7 @@ VERSION 1.2 <2002/11/30> ...@@ -41,6 +41,7 @@ VERSION 1.2 <2002/11/30>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
...@@ -107,7 +108,7 @@ static int multicast_filter_limit = 32; ...@@ -107,7 +108,7 @@ static int multicast_filter_limit = 32;
#define RTL_MIN_IO_SIZE 0x80 #define RTL_MIN_IO_SIZE 0x80
#define RTL8169_TX_TIMEOUT (6*HZ) #define RTL8169_TX_TIMEOUT (6*HZ)
#define RTL8169_PHY_TIMEOUT (HZ) #define RTL8169_PHY_TIMEOUT (10*HZ)
/* write/read MMIO register */ /* write/read MMIO register */
#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) #define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg))
...@@ -341,7 +342,6 @@ struct rtl8169_private { ...@@ -341,7 +342,6 @@ struct rtl8169_private {
struct sk_buff *Rx_skbuff[NUM_RX_DESC]; /* Rx data buffers */ struct sk_buff *Rx_skbuff[NUM_RX_DESC]; /* Rx data buffers */
struct sk_buff *Tx_skbuff[NUM_TX_DESC]; /* Tx data buffers */ struct sk_buff *Tx_skbuff[NUM_TX_DESC]; /* Tx data buffers */
struct timer_list timer; struct timer_list timer;
unsigned long phy_link_down_cnt;
u16 cp_cmd; u16 cp_cmd;
u16 intr_mask; u16 intr_mask;
int phy_auto_nego_reg; int phy_auto_nego_reg;
...@@ -349,6 +349,9 @@ struct rtl8169_private { ...@@ -349,6 +349,9 @@ struct rtl8169_private {
int (*set_speed)(struct net_device *, u8 autoneg, u16 speed, u8 duplex); int (*set_speed)(struct net_device *, u8 autoneg, u16 speed, u8 duplex);
void (*get_settings)(struct net_device *, struct ethtool_cmd *); 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"); MODULE_AUTHOR("Realtek");
...@@ -374,7 +377,7 @@ static int rtl8169_poll(struct net_device *dev, int *budget); ...@@ -374,7 +377,7 @@ static int rtl8169_poll(struct net_device *dev, int *budget);
static const u16 rtl8169_intr_mask = static const u16 rtl8169_intr_mask =
LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK;
static const u16 rtl8169_napi_event = static const u16 rtl8169_napi_event =
RxOK | LinkChg | RxOverflow | RxFIFOOver | TxOK | TxErr; RxOK | RxOverflow | RxFIFOOver | TxOK | TxErr;
static const unsigned int rtl8169_rx_config = static const unsigned int rtl8169_rx_config =
(RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift);
...@@ -416,6 +419,53 @@ static int mdio_read(void *ioaddr, int RegAddr) ...@@ -416,6 +419,53 @@ static int mdio_read(void *ioaddr, int RegAddr)
return value; 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, static void rtl8169_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info) struct ethtool_drvinfo *info)
{ {
...@@ -493,8 +543,14 @@ static int rtl8169_set_speed(struct net_device *dev, ...@@ -493,8 +543,14 @@ static int rtl8169_set_speed(struct net_device *dev,
u8 autoneg, u16 speed, u8 duplex) u8 autoneg, u16 speed, u8 duplex)
{ {
struct rtl8169_private *tp = netdev_priv(dev); 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) 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) ...@@ -753,56 +809,42 @@ static void rtl8169_hw_phy_config(struct net_device *dev)
mdio_write(ioaddr, 31, 0x0000); //w 31 2 0 0 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) static void rtl8169_phy_timer(unsigned long __opaque)
{ {
struct net_device *dev = (struct net_device *)__opaque; struct net_device *dev = (struct net_device *)__opaque;
struct rtl8169_private *tp = netdev_priv(dev); struct rtl8169_private *tp = netdev_priv(dev);
struct timer_list *timer = &tp->timer; struct timer_list *timer = &tp->timer;
void *ioaddr = tp->mmio_addr; void *ioaddr = tp->mmio_addr;
unsigned long timeout = RTL8169_PHY_TIMEOUT;
assert(tp->mac_version > RTL_GIGA_MAC_VER_B); assert(tp->mac_version > RTL_GIGA_MAC_VER_B);
assert(tp->phy_version < RTL_GIGA_PHY_VER_G); assert(tp->phy_version < RTL_GIGA_PHY_VER_G);
if (RTL_R8(PHYstatus) & LinkStatus) if (!(tp->phy_1000_ctrl_reg & PHY_Cap_1000_Full))
tp->phy_link_down_cnt = 0; return;
else {
tp->phy_link_down_cnt++;
if (tp->phy_link_down_cnt >= 12) {
int reg;
// If link on 1000, perform phy reset. spin_lock_irq(&tp->lock);
reg = mdio_read(ioaddr, PHY_1000_CTRL_REG);
if (reg & PHY_Cap_1000_Full)
rtl8169_hw_phy_reset(dev);
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) static inline void rtl8169_delete_timer(struct net_device *dev)
...@@ -815,8 +857,6 @@ 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; return;
del_timer_sync(timer); del_timer_sync(timer);
tp->phy_link_down_cnt = 0;
} }
static inline void rtl8169_request_timer(struct net_device *dev) static inline void rtl8169_request_timer(struct net_device *dev)
...@@ -828,8 +868,6 @@ 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)) (tp->phy_version >= RTL_GIGA_PHY_VER_G))
return; return;
tp->phy_link_down_cnt = 0;
init_timer(timer); init_timer(timer);
timer->expires = jiffies + RTL8169_PHY_TIMEOUT; timer->expires = jiffies + RTL8169_PHY_TIMEOUT;
timer->data = (unsigned long)(dev); timer->data = (unsigned long)(dev);
...@@ -1014,11 +1052,17 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -1014,11 +1052,17 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (RTL_R8(PHYstatus) & TBI_Enable) { if (RTL_R8(PHYstatus) & TBI_Enable) {
tp->set_speed = rtl8169_set_speed_tbi; tp->set_speed = rtl8169_set_speed_tbi;
tp->get_settings = rtl8169_gset_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 */ tp->phy_1000_ctrl_reg = PHY_Cap_1000_Full; /* Implied by TBI */
} else { } else {
tp->set_speed = rtl8169_set_speed_xmii; tp->set_speed = rtl8169_set_speed_xmii;
tp->get_settings = rtl8169_gset_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 // Get MAC address. FIXME: read EEPROM
...@@ -1752,10 +1796,7 @@ rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs) ...@@ -1752,10 +1796,7 @@ rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
break; break;
handled = 1; handled = 1;
/*
if (status & LinkChg)
link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
*/
status &= tp->intr_mask; status &= tp->intr_mask;
RTL_W16(IntrStatus, RTL_W16(IntrStatus,
(status & RxFIFOOver) ? (status | RxOverflow) : status); (status & RxFIFOOver) ? (status | RxOverflow) : status);
...@@ -1763,6 +1804,9 @@ rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs) ...@@ -1763,6 +1804,9 @@ rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
if (!(status & rtl8169_intr_mask)) if (!(status & rtl8169_intr_mask))
break; break;
if (status & LinkChg)
rtl8169_check_link_status(dev, tp, ioaddr);
#ifdef CONFIG_R8169_NAPI #ifdef CONFIG_R8169_NAPI
RTL_W16(IntrMask, rtl8169_intr_mask & ~rtl8169_napi_event); RTL_W16(IntrMask, rtl8169_intr_mask & ~rtl8169_napi_event);
tp->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) ...@@ -1776,7 +1820,7 @@ rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
break; break;
#else #else
// Rx interrupt // Rx interrupt
if (status & (RxOK | LinkChg | RxOverflow | RxFIFOOver)) { if (status & (RxOK | RxOverflow | RxFIFOOver)) {
rtl8169_rx_interrupt(dev, tp, ioaddr); rtl8169_rx_interrupt(dev, tp, ioaddr);
} }
// Tx interrupt // Tx interrupt
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment