summaryrefslogtreecommitdiff
path: root/drivers/net/r8169.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/r8169.c')
-rw-r--r--drivers/net/r8169.c331
1 files changed, 183 insertions, 148 deletions
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 0b6e8c896835..35196faa084e 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -66,7 +66,6 @@ static const int multicast_filter_limit = 32;
#define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
#define EarlyTxThld 0x3F /* 0x3F means NO early transmit */
-#define RxPacketMaxSize 0x3FE8 /* 16K - 1 - ETH_HLEN - VLAN - CRC... */
#define SafeMtu 0x1c20 /* ... actually life sucks beyond ~7k */
#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */
@@ -94,6 +93,7 @@ static const int multicast_filter_limit = 32;
#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg)))
enum mac_version {
+ RTL_GIGA_MAC_NONE = 0x00,
RTL_GIGA_MAC_VER_01 = 0x01, // 8169
RTL_GIGA_MAC_VER_02 = 0x02, // 8169S
RTL_GIGA_MAC_VER_03 = 0x03, // 8110S
@@ -479,7 +479,6 @@ struct rtl8169_private {
u16 intr_event;
u16 napi_event;
u16 intr_mask;
- int phy_auto_nego_reg;
int phy_1000_ctrl_reg;
#ifdef CONFIG_R8169_VLAN
struct vlan_group *vlgrp;
@@ -844,76 +843,81 @@ static int rtl8169_set_speed_xmii(struct net_device *dev,
{
struct rtl8169_private *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->mmio_addr;
- int auto_nego, giga_ctrl;
-
- auto_nego = mdio_read(ioaddr, MII_ADVERTISE);
- auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
- ADVERTISE_100HALF | ADVERTISE_100FULL);
- giga_ctrl = mdio_read(ioaddr, MII_CTRL1000);
- giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
+ int giga_ctrl, bmcr;
if (autoneg == AUTONEG_ENABLE) {
+ int auto_nego;
+
+ auto_nego = mdio_read(ioaddr, MII_ADVERTISE);
auto_nego |= (ADVERTISE_10HALF | ADVERTISE_10FULL |
ADVERTISE_100HALF | ADVERTISE_100FULL);
- giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF;
- } else {
- if (speed == SPEED_10)
- auto_nego |= ADVERTISE_10HALF | ADVERTISE_10FULL;
- else if (speed == SPEED_100)
- auto_nego |= ADVERTISE_100HALF | ADVERTISE_100FULL;
- else if (speed == SPEED_1000)
- giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF;
-
- if (duplex == DUPLEX_HALF)
- auto_nego &= ~(ADVERTISE_10FULL | ADVERTISE_100FULL);
+ auto_nego |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
- if (duplex == DUPLEX_FULL)
- auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_100HALF);
-
- /* This tweak comes straight from Realtek's driver. */
- if ((speed == SPEED_100) && (duplex == DUPLEX_HALF) &&
- ((tp->mac_version == RTL_GIGA_MAC_VER_13) ||
- (tp->mac_version == RTL_GIGA_MAC_VER_16))) {
- auto_nego = ADVERTISE_100HALF | ADVERTISE_CSMA;
- }
- }
+ giga_ctrl = mdio_read(ioaddr, MII_CTRL1000);
+ giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
- /* The 8100e/8101e/8102e do Fast Ethernet only. */
- if ((tp->mac_version == RTL_GIGA_MAC_VER_07) ||
- (tp->mac_version == RTL_GIGA_MAC_VER_08) ||
- (tp->mac_version == RTL_GIGA_MAC_VER_09) ||
- (tp->mac_version == RTL_GIGA_MAC_VER_10) ||
- (tp->mac_version == RTL_GIGA_MAC_VER_13) ||
- (tp->mac_version == RTL_GIGA_MAC_VER_14) ||
- (tp->mac_version == RTL_GIGA_MAC_VER_15) ||
- (tp->mac_version == RTL_GIGA_MAC_VER_16)) {
- if ((giga_ctrl & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)) &&
- netif_msg_link(tp)) {
+ /* The 8100e/8101e/8102e do Fast Ethernet only. */
+ if ((tp->mac_version != RTL_GIGA_MAC_VER_07) &&
+ (tp->mac_version != RTL_GIGA_MAC_VER_08) &&
+ (tp->mac_version != RTL_GIGA_MAC_VER_09) &&
+ (tp->mac_version != RTL_GIGA_MAC_VER_10) &&
+ (tp->mac_version != RTL_GIGA_MAC_VER_13) &&
+ (tp->mac_version != RTL_GIGA_MAC_VER_14) &&
+ (tp->mac_version != RTL_GIGA_MAC_VER_15) &&
+ (tp->mac_version != RTL_GIGA_MAC_VER_16)) {
+ giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF;
+ } else if (netif_msg_link(tp)) {
printk(KERN_INFO "%s: PHY does not support 1000Mbps.\n",
dev->name);
}
- giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
- }
- auto_nego |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+ bmcr = BMCR_ANENABLE | BMCR_ANRESTART;
+
+ if ((tp->mac_version == RTL_GIGA_MAC_VER_11) ||
+ (tp->mac_version == RTL_GIGA_MAC_VER_12) ||
+ (tp->mac_version >= RTL_GIGA_MAC_VER_17)) {
+ /*
+ * Wake up the PHY.
+ * Vendor specific (0x1f) and reserved (0x0e) MII
+ * registers.
+ */
+ mdio_write(ioaddr, 0x1f, 0x0000);
+ mdio_write(ioaddr, 0x0e, 0x0000);
+ }
+
+ mdio_write(ioaddr, MII_ADVERTISE, auto_nego);
+ mdio_write(ioaddr, MII_CTRL1000, giga_ctrl);
+ } else {
+ giga_ctrl = 0;
+
+ if (speed == SPEED_10)
+ bmcr = 0;
+ else if (speed == SPEED_100)
+ bmcr = BMCR_SPEED100;
+ else
+ return -EINVAL;
+
+ if (duplex == DUPLEX_FULL)
+ bmcr |= BMCR_FULLDPLX;
- if ((tp->mac_version == RTL_GIGA_MAC_VER_11) ||
- (tp->mac_version == RTL_GIGA_MAC_VER_12) ||
- (tp->mac_version >= RTL_GIGA_MAC_VER_17)) {
- /*
- * Wake up the PHY.
- * Vendor specific (0x1f) and reserved (0x0e) MII registers.
- */
mdio_write(ioaddr, 0x1f, 0x0000);
- mdio_write(ioaddr, 0x0e, 0x0000);
}
- tp->phy_auto_nego_reg = auto_nego;
tp->phy_1000_ctrl_reg = giga_ctrl;
- mdio_write(ioaddr, MII_ADVERTISE, auto_nego);
- mdio_write(ioaddr, MII_CTRL1000, giga_ctrl);
- mdio_write(ioaddr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+ mdio_write(ioaddr, MII_BMCR, bmcr);
+
+ if ((tp->mac_version == RTL_GIGA_MAC_VER_02) ||
+ (tp->mac_version == RTL_GIGA_MAC_VER_03)) {
+ if ((speed == SPEED_100) && (autoneg != AUTONEG_ENABLE)) {
+ mdio_write(ioaddr, 0x17, 0x2138);
+ mdio_write(ioaddr, 0x0e, 0x0260);
+ } else {
+ mdio_write(ioaddr, 0x17, 0x2108);
+ mdio_write(ioaddr, 0x0e, 0x0000);
+ }
+ }
+
return 0;
}
@@ -1296,7 +1300,8 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp,
{ 0xfc800000, 0x00800000, RTL_GIGA_MAC_VER_02 },
{ 0xfc800000, 0x00000000, RTL_GIGA_MAC_VER_01 },
- { 0x00000000, 0x00000000, RTL_GIGA_MAC_VER_01 } /* Catch-all */
+ /* Catch-all */
+ { 0x00000000, 0x00000000, RTL_GIGA_MAC_NONE }
}, *p = mac_info;
u32 reg;
@@ -1304,12 +1309,6 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp,
while ((reg & p->mask) != p->val)
p++;
tp->mac_version = p->mac_version;
-
- if (p->mask == 0x00000000) {
- struct pci_dev *pdev = tp->pci_dev;
-
- dev_info(&pdev->dev, "unknown MAC (%08x)\n", reg);
- }
}
static void rtl8169_print_mac_version(struct rtl8169_private *tp)
@@ -1885,6 +1884,7 @@ static const struct rtl_cfg_info {
u16 intr_event;
u16 napi_event;
unsigned features;
+ u8 default_ver;
} rtl_cfg_infos [] = {
[RTL_CFG_0] = {
.hw_start = rtl_hw_start_8169,
@@ -1893,7 +1893,8 @@ static const struct rtl_cfg_info {
.intr_event = SYSErr | LinkChg | RxOverflow |
RxFIFOOver | TxErr | TxOK | RxOK | RxErr,
.napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow,
- .features = RTL_FEATURE_GMII
+ .features = RTL_FEATURE_GMII,
+ .default_ver = RTL_GIGA_MAC_VER_01,
},
[RTL_CFG_1] = {
.hw_start = rtl_hw_start_8168,
@@ -1902,7 +1903,8 @@ static const struct rtl_cfg_info {
.intr_event = SYSErr | LinkChg | RxOverflow |
TxErr | TxOK | RxOK | RxErr,
.napi_event = TxErr | TxOK | RxOK | RxOverflow,
- .features = RTL_FEATURE_GMII | RTL_FEATURE_MSI
+ .features = RTL_FEATURE_GMII | RTL_FEATURE_MSI,
+ .default_ver = RTL_GIGA_MAC_VER_11,
},
[RTL_CFG_2] = {
.hw_start = rtl_hw_start_8101,
@@ -1911,7 +1913,8 @@ static const struct rtl_cfg_info {
.intr_event = SYSErr | LinkChg | RxOverflow | PCSTimeout |
RxFIFOOver | TxErr | TxOK | RxOK | RxErr,
.napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow,
- .features = RTL_FEATURE_MSI
+ .features = RTL_FEATURE_MSI,
+ .default_ver = RTL_GIGA_MAC_VER_13,
}
};
@@ -2092,6 +2095,15 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Identify chip attached to board */
rtl8169_get_mac_version(tp, ioaddr);
+ /* Use appropriate default if unknown */
+ if (tp->mac_version == RTL_GIGA_MAC_NONE) {
+ if (netif_msg_probe(tp)) {
+ dev_notice(&pdev->dev,
+ "unknown MAC, using family default\n");
+ }
+ tp->mac_version = cfg->default_ver;
+ }
+
rtl8169_print_mac_version(tp);
for (i = 0; i < ARRAY_SIZE(rtl_chip_info); i++) {
@@ -2099,13 +2111,9 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
break;
}
if (i == ARRAY_SIZE(rtl_chip_info)) {
- /* Unknown chip: assume array element #0, original RTL-8169 */
- if (netif_msg_probe(tp)) {
- dev_printk(KERN_DEBUG, &pdev->dev,
- "unknown chip version, assuming %s\n",
- rtl_chip_info[0].name);
- }
- i = 0;
+ dev_err(&pdev->dev,
+ "driver bug, MAC version not found in rtl_chip_info\n");
+ goto err_out_msi_5;
}
tp->chipset = i;
@@ -2357,10 +2365,10 @@ static u16 rtl_rw_cpluscmd(void __iomem *ioaddr)
return cmd;
}
-static void rtl_set_rx_max_size(void __iomem *ioaddr)
+static void rtl_set_rx_max_size(void __iomem *ioaddr, unsigned int rx_buf_sz)
{
/* Low hurts. Let's disable the filtering. */
- RTL_W16(RxMaxSize, 16383);
+ RTL_W16(RxMaxSize, rx_buf_sz);
}
static void rtl8169_set_magic_reg(void __iomem *ioaddr, unsigned mac_version)
@@ -2407,7 +2415,7 @@ static void rtl_hw_start_8169(struct net_device *dev)
RTL_W8(EarlyTxThres, EarlyTxThld);
- rtl_set_rx_max_size(ioaddr);
+ rtl_set_rx_max_size(ioaddr, tp->rx_buf_sz);
if ((tp->mac_version == RTL_GIGA_MAC_VER_01) ||
(tp->mac_version == RTL_GIGA_MAC_VER_02) ||
@@ -2668,7 +2676,7 @@ static void rtl_hw_start_8168(struct net_device *dev)
RTL_W8(EarlyTxThres, EarlyTxThld);
- rtl_set_rx_max_size(ioaddr);
+ rtl_set_rx_max_size(ioaddr, tp->rx_buf_sz);
tp->cp_cmd |= RTL_R16(CPlusCmd) | PktCntrDisable | INTT_1;
@@ -2846,7 +2854,7 @@ static void rtl_hw_start_8101(struct net_device *dev)
RTL_W8(EarlyTxThres, EarlyTxThld);
- rtl_set_rx_max_size(ioaddr);
+ rtl_set_rx_max_size(ioaddr, tp->rx_buf_sz);
tp->cp_cmd |= rtl_rw_cpluscmd(ioaddr) | PCIMulRW;
@@ -3270,8 +3278,6 @@ static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev)
status = opts1 | len | (RingEnd * !((entry + 1) % NUM_TX_DESC));
txd->opts1 = cpu_to_le32(status);
- dev->trans_start = jiffies;
-
tp->cur_tx += frags + 1;
smp_wmb();
@@ -3372,7 +3378,7 @@ static void rtl8169_tx_interrupt(struct net_device *dev,
rtl8169_unmap_tx_skb(tp->pci_dev, tx_skb, tp->TxDescArray + entry);
if (status & LastFrag) {
- dev_kfree_skb_irq(tx_skb->skb);
+ dev_kfree_skb(tx_skb->skb);
tx_skb->skb = NULL;
}
dirty_tx++;
@@ -3554,54 +3560,64 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
int handled = 0;
int status;
+ /* loop handling interrupts until we have no new ones or
+ * we hit a invalid/hotplug case.
+ */
status = RTL_R16(IntrStatus);
+ while (status && status != 0xffff) {
+ handled = 1;
- /* hotplug/major error/no more work/shared irq */
- if ((status == 0xffff) || !status)
- goto out;
-
- handled = 1;
+ /* Handle all of the error cases first. These will reset
+ * the chip, so just exit the loop.
+ */
+ if (unlikely(!netif_running(dev))) {
+ rtl8169_asic_down(ioaddr);
+ break;
+ }
- if (unlikely(!netif_running(dev))) {
- rtl8169_asic_down(ioaddr);
- goto out;
- }
+ /* Work around for rx fifo overflow */
+ if (unlikely(status & RxFIFOOver) &&
+ (tp->mac_version == RTL_GIGA_MAC_VER_11)) {
+ netif_stop_queue(dev);
+ rtl8169_tx_timeout(dev);
+ break;
+ }
- status &= tp->intr_mask;
- RTL_W16(IntrStatus,
- (status & RxFIFOOver) ? (status | RxOverflow) : status);
+ if (unlikely(status & SYSErr)) {
+ rtl8169_pcierr_interrupt(dev);
+ break;
+ }
- if (!(status & tp->intr_event))
- goto out;
+ if (status & LinkChg)
+ rtl8169_check_link_status(dev, tp, ioaddr);
- /* Work around for rx fifo overflow */
- if (unlikely(status & RxFIFOOver) &&
- (tp->mac_version == RTL_GIGA_MAC_VER_11)) {
- netif_stop_queue(dev);
- rtl8169_tx_timeout(dev);
- goto out;
- }
+ /* We need to see the lastest version of tp->intr_mask to
+ * avoid ignoring an MSI interrupt and having to wait for
+ * another event which may never come.
+ */
+ smp_rmb();
+ if (status & tp->intr_mask & tp->napi_event) {
+ RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event);
+ tp->intr_mask = ~tp->napi_event;
+
+ if (likely(napi_schedule_prep(&tp->napi)))
+ __napi_schedule(&tp->napi);
+ else if (netif_msg_intr(tp)) {
+ printk(KERN_INFO "%s: interrupt %04x in poll\n",
+ dev->name, status);
+ }
+ }
- if (unlikely(status & SYSErr)) {
- rtl8169_pcierr_interrupt(dev);
- goto out;
+ /* We only get a new MSI interrupt when all active irq
+ * sources on the chip have been acknowledged. So, ack
+ * everything we've seen and check if new sources have become
+ * active to avoid blocking all interrupts from the chip.
+ */
+ RTL_W16(IntrStatus,
+ (status & RxFIFOOver) ? (status | RxOverflow) : status);
+ status = RTL_R16(IntrStatus);
}
- if (status & LinkChg)
- rtl8169_check_link_status(dev, tp, ioaddr);
-
- if (status & tp->napi_event) {
- RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event);
- tp->intr_mask = ~tp->napi_event;
-
- if (likely(napi_schedule_prep(&tp->napi)))
- __napi_schedule(&tp->napi);
- else if (netif_msg_intr(tp)) {
- printk(KERN_INFO "%s: interrupt %04x in poll\n",
- dev->name, status);
- }
- }
-out:
return IRQ_RETVAL(handled);
}
@@ -3617,13 +3633,15 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
if (work_done < budget) {
napi_complete(napi);
- tp->intr_mask = 0xffff;
- /*
- * 20040426: the barrier is not strictly required but the
- * behavior of the irq handler could be less predictable
- * without it. Btw, the lack of flush for the posted pci
- * write is safe - FR
+
+ /* We need for force the visibility of tp->intr_mask
+ * for other CPUs, as we can loose an MSI interrupt
+ * and potentially wait for a retransmit timeout if we don't.
+ * The posted write to IntrMask is safe, as it will
+ * eventually make it to the chip and we won't loose anything
+ * until it does.
*/
+ tp->intr_mask = 0xffff;
smp_wmb();
RTL_W16(IntrMask, tp->intr_event);
}
@@ -3791,16 +3809,13 @@ static struct net_device_stats *rtl8169_get_stats(struct net_device *dev)
return &dev->stats;
}
-#ifdef CONFIG_PM
-
-static int rtl8169_suspend(struct pci_dev *pdev, pm_message_t state)
+static void rtl8169_net_suspend(struct net_device *dev)
{
- struct net_device *dev = pci_get_drvdata(pdev);
struct rtl8169_private *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->mmio_addr;
if (!netif_running(dev))
- goto out_pci_suspend;
+ return;
netif_device_detach(dev);
netif_stop_queue(dev);
@@ -3812,24 +3827,25 @@ static int rtl8169_suspend(struct pci_dev *pdev, pm_message_t state)
rtl8169_rx_missed(dev, ioaddr);
spin_unlock_irq(&tp->lock);
+}
-out_pci_suspend:
- pci_save_state(pdev);
- pci_enable_wake(pdev, pci_choose_state(pdev, state),
- (tp->features & RTL_FEATURE_WOL) ? 1 : 0);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
+#ifdef CONFIG_PM
+
+static int rtl8169_suspend(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ rtl8169_net_suspend(dev);
return 0;
}
-static int rtl8169_resume(struct pci_dev *pdev)
+static int rtl8169_resume(struct device *device)
{
+ struct pci_dev *pdev = to_pci_dev(device);
struct net_device *dev = pci_get_drvdata(pdev);
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
- pci_enable_wake(pdev, PCI_D0, 0);
-
if (!netif_running(dev))
goto out;
@@ -3840,23 +3856,42 @@ out:
return 0;
}
+static struct dev_pm_ops rtl8169_pm_ops = {
+ .suspend = rtl8169_suspend,
+ .resume = rtl8169_resume,
+ .freeze = rtl8169_suspend,
+ .thaw = rtl8169_resume,
+ .poweroff = rtl8169_suspend,
+ .restore = rtl8169_resume,
+};
+
+#define RTL8169_PM_OPS (&rtl8169_pm_ops)
+
+#else /* !CONFIG_PM */
+
+#define RTL8169_PM_OPS NULL
+
+#endif /* !CONFIG_PM */
+
static void rtl_shutdown(struct pci_dev *pdev)
{
- rtl8169_suspend(pdev, PMSG_SUSPEND);
-}
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ rtl8169_net_suspend(dev);
-#endif /* CONFIG_PM */
+ if (system_state == SYSTEM_POWER_OFF) {
+ pci_wake_from_d3(pdev, true);
+ pci_set_power_state(pdev, PCI_D3hot);
+ }
+}
static struct pci_driver rtl8169_pci_driver = {
.name = MODULENAME,
.id_table = rtl8169_pci_tbl,
.probe = rtl8169_init_one,
.remove = __devexit_p(rtl8169_remove_one),
-#ifdef CONFIG_PM
- .suspend = rtl8169_suspend,
- .resume = rtl8169_resume,
.shutdown = rtl_shutdown,
-#endif
+ .driver.pm = RTL8169_PM_OPS,
};
static int __init rtl8169_init_module(void)