BACKPORT: r8169: fix auto speed down issue
[cascardo/linux.git] / drivers / net / ethernet / realtek / r8169.c
index f545093..07a2a88 100644 (file)
 #define R8169_MSG_DEFAULT \
        (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN)
 
-#define TX_BUFFS_AVAIL(tp) \
-       (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx - 1)
+#define TX_SLOTS_AVAIL(tp) \
+       (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx)
+
+/* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */
+#define TX_FRAGS_READY_FOR(tp,nr_frags) \
+       (TX_SLOTS_AVAIL(tp) >= (nr_frags + 1))
 
 /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
    The RTL chips use a 64 element hash table based on the Ethernet CRC. */
@@ -315,6 +319,8 @@ enum rtl_registers {
        Config0         = 0x51,
        Config1         = 0x52,
        Config2         = 0x53,
+#define PME_SIGNAL                     (1 << 5)        /* 8168c and later */
+
        Config3         = 0x54,
        Config4         = 0x55,
        Config5         = 0x56,
@@ -700,6 +706,7 @@ struct rtl8169_private {
        u16 cp_cmd;
 
        u16 event_slow;
+       bool runtime_suspended;
 
        struct mdio_ops {
                void (*write)(void __iomem *, int, int);
@@ -752,6 +759,10 @@ struct rtl8169_private {
 #define RTL_FIRMWARE_UNKNOWN   ERR_PTR(-EAGAIN)
 };
 
+static int aspm_disable = 0;
+module_param(aspm_disable, int, 0444);
+MODULE_PARM_DESC(aspm_disable, "Disable ASPM completely.");
+
 MODULE_AUTHOR("Realtek and the Linux r8169 crew <netdev@vger.kernel.org>");
 MODULE_DESCRIPTION("RealTek RTL-8169 Gigabit Ethernet driver");
 module_param(use_dac, int, 0);
@@ -1396,7 +1407,6 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
                u16 reg;
                u8  mask;
        } cfg[] = {
-               { WAKE_ANY,   Config1, PMEnable },
                { WAKE_PHY,   Config3, LinkUp },
                { WAKE_MAGIC, Config3, MagicPacket },
                { WAKE_UCAST, Config5, UWF },
@@ -1404,16 +1414,32 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
                { WAKE_MCAST, Config5, MWF },
                { WAKE_ANY,   Config5, LanWake }
        };
+       u8 options;
 
        RTL_W8(Cfg9346, Cfg9346_Unlock);
 
        for (i = 0; i < ARRAY_SIZE(cfg); i++) {
-               u8 options = RTL_R8(cfg[i].reg) & ~cfg[i].mask;
+               options = RTL_R8(cfg[i].reg) & ~cfg[i].mask;
                if (wolopts & cfg[i].opt)
                        options |= cfg[i].mask;
                RTL_W8(cfg[i].reg, options);
        }
 
+       switch (tp->mac_version) {
+       case RTL_GIGA_MAC_VER_01 ... RTL_GIGA_MAC_VER_17:
+               options = RTL_R8(Config1) & ~PMEnable;
+               if (wolopts)
+                       options |= PMEnable;
+               RTL_W8(Config1, options);
+               break;
+       default:
+               options = RTL_R8(Config2) & ~PME_SIGNAL;
+               if (wolopts)
+                       options |= PME_SIGNAL;
+               RTL_W8(Config2, options);
+               break;
+       }
+
        RTL_W8(Cfg9346, Cfg9346_Lock);
 }
 
@@ -3462,6 +3488,30 @@ static void __devinit rtl_init_mdio_ops(struct rtl8169_private *tp)
        }
 }
 
+static void rtl_speed_down(struct rtl8169_private *tp)
+{
+       u32 adv;
+       int lpa;
+
+       rtl_writephy(tp, 0x1f, 0x0000);
+       lpa = rtl_readphy(tp, MII_LPA);
+
+       if (lpa & (LPA_10HALF | LPA_10FULL))
+               adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full;
+       else if (lpa & (LPA_100HALF | LPA_100FULL))
+               adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+                     ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
+       else
+               adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+                     ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |
+                     (tp->mii.supports_gmii ?
+                      ADVERTISED_1000baseT_Half |
+                      ADVERTISED_1000baseT_Full : 0);
+
+       rtl8169_set_speed(tp->dev, AUTONEG_ENABLE, SPEED_1000, DUPLEX_FULL,
+                         adv);
+}
+
 static void rtl_wol_suspend_quirk(struct rtl8169_private *tp)
 {
        void __iomem *ioaddr = tp->mmio_addr;
@@ -3485,9 +3535,7 @@ static bool rtl_wol_pll_power_down(struct rtl8169_private *tp)
        if (!(__rtl8169_get_wol(tp) & WAKE_ANY))
                return false;
 
-       rtl_writephy(tp, 0x1f, 0x0000);
-       rtl_writephy(tp, MII_BMCR, 0x0000);
-
+       rtl_speed_down(tp);
        rtl_wol_suspend_quirk(tp);
 
        return true;
@@ -5115,7 +5163,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
        u32 opts[2];
        int frags;
 
-       if (unlikely(TX_BUFFS_AVAIL(tp) < skb_shinfo(skb)->nr_frags)) {
+       if (unlikely(!TX_FRAGS_READY_FOR(tp, skb_shinfo(skb)->nr_frags))) {
                netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!\n");
                goto err_stop_0;
        }
@@ -5169,7 +5217,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
 
        mmiowb();
 
-       if (TX_BUFFS_AVAIL(tp) < MAX_SKB_FRAGS) {
+       if (!TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) {
                /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must
                 * not miss a ring update when it notices a stopped queue.
                 */
@@ -5183,7 +5231,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
                 * can't.
                 */
                smp_mb();
-               if (TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS)
+               if (TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS))
                        netif_wake_queue(dev);
        }
 
@@ -5306,7 +5354,7 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
                 */
                smp_mb();
                if (netif_queue_stopped(dev) &&
-                   (TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS)) {
+                   TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) {
                        netif_wake_queue(dev);
                }
                /*
@@ -5716,6 +5764,7 @@ static int rtl_open(struct net_device *dev)
        rtl_unlock_work(tp);
 
        tp->saved_wolopts = 0;
+       tp->runtime_suspended = false;
        pm_runtime_put_noidle(&pdev->dev);
 
        rtl8169_check_link_status(dev, tp, ioaddr);
@@ -5796,6 +5845,11 @@ static int rtl8169_suspend(struct device *device)
 {
        struct pci_dev *pdev = to_pci_dev(device);
        struct net_device *dev = pci_get_drvdata(pdev);
+       struct rtl8169_private *tp = netdev_priv(dev);
+
+       rtl_lock_work(tp);
+       tp->saved_wolopts = __rtl8169_get_wol(tp);
+       rtl_unlock_work(tp);
 
        rtl8169_net_suspend(dev);
 
@@ -5824,6 +5878,10 @@ static int rtl8169_resume(struct device *device)
        struct net_device *dev = pci_get_drvdata(pdev);
        struct rtl8169_private *tp = netdev_priv(dev);
 
+       rtl_lock_work(tp);
+       __rtl8169_set_wol(tp, tp->saved_wolopts);
+       rtl_unlock_work(tp);
+
        rtl8169_init_phy(dev, tp);
 
        if (netif_running(dev))
@@ -5844,6 +5902,7 @@ static int rtl8169_runtime_suspend(struct device *device)
        rtl_lock_work(tp);
        tp->saved_wolopts = __rtl8169_get_wol(tp);
        __rtl8169_set_wol(tp, WAKE_ANY);
+       tp->runtime_suspended = true;
        rtl_unlock_work(tp);
 
        rtl8169_net_suspend(dev);
@@ -5863,6 +5922,7 @@ static int rtl8169_runtime_resume(struct device *device)
        rtl_lock_work(tp);
        __rtl8169_set_wol(tp, tp->saved_wolopts);
        tp->saved_wolopts = 0;
+       tp->runtime_suspended = false;
        rtl_unlock_work(tp);
 
        rtl8169_init_phy(dev, tp);
@@ -5878,6 +5938,7 @@ static int rtl8169_runtime_idle(struct device *device)
        struct net_device *dev = pci_get_drvdata(pdev);
        struct rtl8169_private *tp = netdev_priv(dev);
 
+       __rtl8169_check_link_status(dev, tp, tp->mmio_addr, true);
        return tp->TxDescArray ? -EBUSY : 0;
 }
 
@@ -5929,6 +5990,10 @@ static void rtl_shutdown(struct pci_dev *pdev)
 
        pm_runtime_get_sync(d);
 
+       /* Get the device back to D0 state if it was runtime suspended. */
+       if (tp->runtime_suspended)
+               pci_set_power_state(pdev, PCI_D0);
+
        rtl8169_net_suspend(dev);
 
        /* Restore original MAC address */
@@ -5936,6 +6001,10 @@ static void rtl_shutdown(struct pci_dev *pdev)
 
        rtl8169_hw_reset(tp);
 
+       /* Restore WOL flags if they were messed around with. */
+       if (tp->saved_wolopts)
+               __rtl8169_set_wol(tp, tp->saved_wolopts);
+
        if (system_state == SYSTEM_POWER_OFF) {
                if (__rtl8169_get_wol(tp) & WAKE_ANY) {
                        rtl_wol_suspend_quirk(tp);
@@ -6048,8 +6117,7 @@ static unsigned rtl_try_msi(struct rtl8169_private *tp,
                        msi = RTL_FEATURE_MSI;
                }
        }
-       if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
-               RTL_W8(Config2, cfg2);
+       RTL_W8(Config2, cfg2);
        return msi;
 }
 
@@ -6093,8 +6161,12 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        /* disable ASPM completely as that cause random device stop working
         * problems as well as full system hangs for some PCIe devices users */
-       pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
-                                    PCIE_LINK_STATE_CLKPM);
+       if (aspm_disable) {
+               pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
+                                      PCIE_LINK_STATE_L1 |
+                                      PCIE_LINK_STATE_CLKPM);
+               dprintk("ASPM disabled");
+       }
 
        /* enable device (incl. PCI PM wakeup and hotplug setup) */
        rc = pci_enable_device(pdev);
@@ -6186,11 +6258,8 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        RTL_W8(Cfg9346, Cfg9346_Unlock);
        RTL_W8(Config1, RTL_R8(Config1) | PMEnable);
-       RTL_W8(Config5, RTL_R8(Config5) & PMEStatus);
-       if ((RTL_R8(Config3) & (LinkUp | MagicPacket)) != 0)
-               tp->features |= RTL_FEATURE_WOL;
-       if ((RTL_R8(Config5) & (UWF | BWF | MWF)) != 0)
-               tp->features |= RTL_FEATURE_WOL;
+       RTL_W8(Config3, 0);
+       RTL_W8(Config5, PMEStatus);
        tp->features |= rtl_try_msi(tp, cfg);
        RTL_W8(Cfg9346, Cfg9346_Lock);