atl1c: Add AR8151 v2 support and change L0s/L1 routine
authorJie Yang <Jie.Yang@atheros.com>
Tue, 1 Jun 2010 07:28:12 +0000 (00:28 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 1 Jun 2010 07:28:12 +0000 (00:28 -0700)
Add AR8151 v2.0 Gigabit 1000 support
Change jumbo frame size to 6K
Update L0s/L1 rountine
        when link speed is 100M or 1G, set L1 link timer to 4 for l1d_2 and l2c_b2
        set L1 link timer to 7 for l2c_b, set L1 link timer to 0xF for others.
Update atl1c_suspend routine
just refactory the function, add atl1c_phy_power_saving routine,
when Wake On Lan enable, this func will be called to save power,
it will reautoneg PHY to 10/100M speed depend on the link
partners link capability.
Update atl1c_configure_des_ring
        do not use l2c_b default SRAM configuration.

Signed-off-by: Jie Yang <Jie.Yang@atheros.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/atl1c/atl1c.h
drivers/net/atl1c/atl1c_hw.c
drivers/net/atl1c/atl1c_hw.h
drivers/net/atl1c/atl1c_main.c

index 84ae905..52abbbd 100644 (file)
@@ -73,7 +73,8 @@
 #define FULL_DUPLEX        2
 
 #define AT_RX_BUF_SIZE         (ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN)
-#define MAX_JUMBO_FRAME_SIZE   (9*1024)
+#define MAX_JUMBO_FRAME_SIZE   (6*1024)
+#define MAX_TSO_FRAME_SIZE      (7*1024)
 #define MAX_TX_OFFLOAD_THRESH  (9*1024)
 
 #define AT_MAX_RECEIVE_QUEUE    4
 #define AT_MAX_INT_WORK                5
 #define AT_TWSI_EEPROM_TIMEOUT         100
 #define AT_HW_MAX_IDLE_DELAY   10
-#define AT_SUSPEND_LINK_TIMEOUT 28
+#define AT_SUSPEND_LINK_TIMEOUT 100
 
 #define AT_ASPM_L0S_TIMER      6
 #define AT_ASPM_L1_TIMER       12
+#define AT_LCKDET_TIMER                12
 
 #define ATL1C_PCIE_L0S_L1_DISABLE      0x01
 #define ATL1C_PCIE_PHY_RESET           0x02
@@ -316,6 +318,7 @@ enum atl1c_nic_type {
        athr_l2c_b,
        athr_l2c_b2,
        athr_l1d,
+       athr_l1d_2,
 };
 
 enum atl1c_trans_queue {
@@ -392,6 +395,8 @@ struct atl1c_hw {
        u16 subsystem_id;
        u16 subsystem_vendor_id;
        u8 revision_id;
+       u16 phy_id1;
+       u16 phy_id2;
 
        u32 intr_mask;
        u8 dmaw_dly_cnt;
index f1389d6..d8501f0 100644 (file)
@@ -37,6 +37,9 @@ int atl1c_check_eeprom_exist(struct atl1c_hw *hw)
        if (data & TWSI_DEBUG_DEV_EXIST)
                return 1;
 
+       AT_READ_REG(hw, REG_MASTER_CTRL, &data);
+       if (data & MASTER_CTRL_OTP_SEL)
+               return 1;
        return 0;
 }
 
@@ -69,6 +72,8 @@ static int atl1c_get_permanent_address(struct atl1c_hw *hw)
        u32 i;
        u32 otp_ctrl_data;
        u32 twsi_ctrl_data;
+       u32 ltssm_ctrl_data;
+       u32 wol_data;
        u8  eth_addr[ETH_ALEN];
        u16 phy_data;
        bool raise_vol = false;
@@ -104,6 +109,15 @@ static int atl1c_get_permanent_address(struct atl1c_hw *hw)
                        udelay(20);
                        raise_vol = true;
                }
+               /* close open bit of ReadOnly*/
+               AT_READ_REG(hw, REG_LTSSM_ID_CTRL, &ltssm_ctrl_data);
+               ltssm_ctrl_data &= ~LTSSM_ID_EN_WRO;
+               AT_WRITE_REG(hw, REG_LTSSM_ID_CTRL, ltssm_ctrl_data);
+
+               /* clear any WOL settings */
+               AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
+               AT_READ_REG(hw, REG_WOL_CTRL, &wol_data);
+
 
                AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data);
                twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART;
@@ -119,17 +133,15 @@ static int atl1c_get_permanent_address(struct atl1c_hw *hw)
        }
        /* Disable OTP_CLK */
        if ((hw->nic_type == athr_l1c || hw->nic_type == athr_l2c)) {
-               if (otp_ctrl_data & OTP_CTRL_CLK_EN) {
-                       otp_ctrl_data &= ~OTP_CTRL_CLK_EN;
-                       AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
-                       AT_WRITE_FLUSH(hw);
-                       msleep(1);
-               }
+               otp_ctrl_data &= ~OTP_CTRL_CLK_EN;
+               AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
+               msleep(1);
        }
        if (raise_vol) {
                if (hw->nic_type == athr_l2c_b ||
                    hw->nic_type == athr_l2c_b2 ||
-                   hw->nic_type == athr_l1d) {
+                   hw->nic_type == athr_l1d ||
+                   hw->nic_type == athr_l1d_2) {
                        atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x00);
                        if (atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data))
                                goto out;
@@ -456,14 +468,22 @@ int atl1c_phy_reset(struct atl1c_hw *hw)
 
        if (hw->nic_type == athr_l2c_b ||
            hw->nic_type == athr_l2c_b2 ||
-           hw->nic_type == athr_l1d) {
+           hw->nic_type == athr_l1d ||
+           hw->nic_type == athr_l1d_2) {
                atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x3B);
                atl1c_read_phy_reg(hw, MII_DBG_DATA, &phy_data);
                atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data & 0xFFF7);
                msleep(20);
        }
-
-       /*Enable PHY LinkChange Interrupt */
+       if (hw->nic_type == athr_l1d) {
+               atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x29);
+               atl1c_write_phy_reg(hw, MII_DBG_DATA, 0x929D);
+       }
+       if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c_b2
+               || hw->nic_type == athr_l2c || hw->nic_type == athr_l2c) {
+               atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x29);
+               atl1c_write_phy_reg(hw, MII_DBG_DATA, 0xB6DD);
+       }
        err = atl1c_write_phy_reg(hw, MII_IER, mii_ier_data);
        if (err) {
                if (netif_msg_hw(adapter))
@@ -482,12 +502,10 @@ int atl1c_phy_init(struct atl1c_hw *hw)
        struct pci_dev *pdev = adapter->pdev;
        int ret_val;
        u16 mii_bmcr_data = BMCR_RESET;
-       u16 phy_id1, phy_id2;
 
-       if ((atl1c_read_phy_reg(hw, MII_PHYSID1, &phy_id1) != 0) ||
-               (atl1c_read_phy_reg(hw, MII_PHYSID2, &phy_id2) != 0)) {
-                       if (netif_msg_link(adapter))
-                               dev_err(&pdev->dev, "Error get phy ID\n");
+       if ((atl1c_read_phy_reg(hw, MII_PHYSID1, &hw->phy_id1) != 0) ||
+               (atl1c_read_phy_reg(hw, MII_PHYSID2, &hw->phy_id2) != 0)) {
+               dev_err(&pdev->dev, "Error get phy ID\n");
                return -1;
        }
        switch (hw->media_type) {
@@ -572,6 +590,65 @@ int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex)
        return 0;
 }
 
+int atl1c_phy_power_saving(struct atl1c_hw *hw)
+{
+       struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter;
+       struct pci_dev *pdev = adapter->pdev;
+       int ret = 0;
+       u16 autoneg_advertised = ADVERTISED_10baseT_Half;
+       u16 save_autoneg_advertised;
+       u16 phy_data;
+       u16 mii_lpa_data;
+       u16 speed = SPEED_0;
+       u16 duplex = FULL_DUPLEX;
+       int i;
+
+       atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
+       atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
+       if (phy_data & BMSR_LSTATUS) {
+               atl1c_read_phy_reg(hw, MII_LPA, &mii_lpa_data);
+               if (mii_lpa_data & LPA_10FULL)
+                       autoneg_advertised = ADVERTISED_10baseT_Full;
+               else if (mii_lpa_data & LPA_10HALF)
+                       autoneg_advertised = ADVERTISED_10baseT_Half;
+               else if (mii_lpa_data & LPA_100HALF)
+                       autoneg_advertised = ADVERTISED_100baseT_Half;
+               else if (mii_lpa_data & LPA_100FULL)
+                       autoneg_advertised = ADVERTISED_100baseT_Full;
+
+               save_autoneg_advertised = hw->autoneg_advertised;
+               hw->phy_configured = false;
+               hw->autoneg_advertised = autoneg_advertised;
+               if (atl1c_restart_autoneg(hw) != 0) {
+                       dev_dbg(&pdev->dev, "phy autoneg failed\n");
+                       ret = -1;
+               }
+               hw->autoneg_advertised = save_autoneg_advertised;
+
+               if (mii_lpa_data) {
+                       for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) {
+                               mdelay(100);
+                               atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
+                               atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
+                               if (phy_data & BMSR_LSTATUS) {
+                                       if (atl1c_get_speed_and_duplex(hw, &speed,
+                                                                       &duplex) != 0)
+                                               dev_dbg(&pdev->dev,
+                                                       "get speed and duplex failed\n");
+                                       break;
+                               }
+                       }
+               }
+       } else {
+               speed = SPEED_10;
+               duplex = HALF_DUPLEX;
+       }
+       adapter->link_speed = speed;
+       adapter->link_duplex = duplex;
+
+       return ret;
+}
+
 int atl1c_restart_autoneg(struct atl1c_hw *hw)
 {
        int err = 0;
index 1eeb3ed..3dd6759 100644 (file)
@@ -42,7 +42,7 @@ bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value);
 int atl1c_phy_init(struct atl1c_hw *hw);
 int atl1c_check_eeprom_exist(struct atl1c_hw *hw);
 int atl1c_restart_autoneg(struct atl1c_hw *hw);
-
+int atl1c_phy_power_saving(struct atl1c_hw *hw);
 /* register definition */
 #define REG_DEVICE_CAP                 0x5C
 #define DEVICE_CAP_MAX_PAYLOAD_MASK     0x7
@@ -120,6 +120,12 @@ int atl1c_restart_autoneg(struct atl1c_hw *hw);
 #define REG_PCIE_PHYMISC               0x1000
 #define PCIE_PHYMISC_FORCE_RCV_DET     0x4
 
+#define REG_PCIE_PHYMISC2              0x1004
+#define PCIE_PHYMISC2_SERDES_CDR_MASK  0x3
+#define PCIE_PHYMISC2_SERDES_CDR_SHIFT 16
+#define PCIE_PHYMISC2_SERDES_TH_MASK   0x3
+#define PCIE_PHYMISC2_SERDES_TH_SHIFT  18
+
 #define REG_TWSI_DEBUG                 0x1108
 #define TWSI_DEBUG_DEV_EXIST           0x20000000
 
@@ -150,24 +156,28 @@ int atl1c_restart_autoneg(struct atl1c_hw *hw);
 #define PM_CTRL_ASPM_L0S_EN            0x00001000
 #define PM_CTRL_CLK_SWH_L1             0x00002000
 #define PM_CTRL_CLK_PWM_VER1_1         0x00004000
-#define PM_CTRL_PCIE_RECV              0x00008000
+#define PM_CTRL_RCVR_WT_TIMER          0x00008000
 #define PM_CTRL_L1_ENTRY_TIMER_MASK    0xF
 #define PM_CTRL_L1_ENTRY_TIMER_SHIFT   16
 #define PM_CTRL_PM_REQ_TIMER_MASK      0xF
 #define PM_CTRL_PM_REQ_TIMER_SHIFT     20
-#define PM_CTRL_LCKDET_TIMER_MASK      0x3F
+#define PM_CTRL_LCKDET_TIMER_MASK      0xF
 #define PM_CTRL_LCKDET_TIMER_SHIFT     24
 #define PM_CTRL_EN_BUFS_RX_L0S         0x10000000
 #define PM_CTRL_SA_DLY_EN              0x20000000
 #define PM_CTRL_MAC_ASPM_CHK           0x40000000
 #define PM_CTRL_HOTRST                 0x80000000
 
+#define REG_LTSSM_ID_CTRL              0x12FC
+#define LTSSM_ID_EN_WRO                        0x1000
 /* Selene Master Control Register */
 #define REG_MASTER_CTRL                        0x1400
 #define MASTER_CTRL_SOFT_RST            0x1
 #define MASTER_CTRL_TEST_MODE_MASK     0x3
 #define MASTER_CTRL_TEST_MODE_SHIFT    2
 #define MASTER_CTRL_BERT_START         0x10
+#define MASTER_CTRL_OOB_DIS_OFF                0x40
+#define MASTER_CTRL_SA_TIMER_EN                0x80
 #define MASTER_CTRL_MTIMER_EN           0x100
 #define MASTER_CTRL_MANUAL_INT          0x200
 #define MASTER_CTRL_TX_ITIMER_EN       0x400
@@ -220,6 +230,12 @@ int atl1c_restart_autoneg(struct atl1c_hw *hw);
                GPHY_CTRL_PWDOWN_HW     |\
                GPHY_CTRL_PHY_IDDQ)
 
+#define GPHY_CTRL_POWER_SAVING (       \
+               GPHY_CTRL_SEL_ANA_RST   |\
+               GPHY_CTRL_HIB_EN        |\
+               GPHY_CTRL_HIB_PULSE     |\
+               GPHY_CTRL_PWDOWN_HW     |\
+               GPHY_CTRL_PHY_IDDQ)
 /* Block IDLE Status Register */
 #define REG_IDLE_STATUS                0x1410
 #define IDLE_STATUS_MASK               0x00FF
@@ -287,6 +303,14 @@ int atl1c_restart_autoneg(struct atl1c_hw *hw);
 #define SERDES_LOCK_DETECT             0x1  /* SerDes lock detected. This signal
                                              * comes from Analog SerDes */
 #define SERDES_LOCK_DETECT_EN          0x2  /* 1: Enable SerDes Lock detect function */
+#define SERDES_LOCK_STS_SELFB_PLL_SHIFT 0xE
+#define SERDES_LOCK_STS_SELFB_PLL_MASK  0x3
+#define SERDES_OVCLK_18_25             0x0
+#define SERDES_OVCLK_12_18             0x1
+#define SERDES_OVCLK_0_4               0x2
+#define SERDES_OVCLK_4_12              0x3
+#define SERDES_MAC_CLK_SLOWDOWN                0x20000
+#define SERDES_PYH_CLK_SLOWDOWN                0x40000
 
 /* MAC Control Register  */
 #define REG_MAC_CTRL                   0x1480
@@ -693,6 +717,21 @@ int atl1c_restart_autoneg(struct atl1c_hw *hw);
 #define REG_MAC_TX_STATUS_BIN          0x1760
 #define REG_MAC_TX_STATUS_END          0x17c0
 
+#define REG_CLK_GATING_CTRL            0x1814
+#define CLK_GATING_DMAW_EN             0x0001
+#define CLK_GATING_DMAR_EN             0x0002
+#define CLK_GATING_TXQ_EN              0x0004
+#define CLK_GATING_RXQ_EN              0x0008
+#define CLK_GATING_TXMAC_EN            0x0010
+#define CLK_GATING_RXMAC_EN            0x0020
+
+#define CLK_GATING_EN_ALL      (CLK_GATING_DMAW_EN |\
+                                CLK_GATING_DMAR_EN |\
+                                CLK_GATING_TXQ_EN  |\
+                                CLK_GATING_RXQ_EN  |\
+                                CLK_GATING_TXMAC_EN|\
+                                CLK_GATING_RXMAC_EN)
+
 /* DEBUG ADDR */
 #define REG_DEBUG_DATA0                0x1900
 #define REG_DEBUG_DATA1                0x1904
@@ -734,6 +773,10 @@ int atl1c_restart_autoneg(struct atl1c_hw *hw);
 
 #define MII_PHYSID1                    0x02
 #define MII_PHYSID2                    0x03
+#define L1D_MPW_PHYID1                 0xD01C  /* V7 */
+#define L1D_MPW_PHYID2                 0xD01D  /* V1-V6 */
+#define L1D_MPW_PHYID3                 0xD01E  /* V8 */
+
 
 /* Autoneg Advertisement Register */
 #define MII_ADVERTISE                  0x04
index 1c3c046..c7b8ef5 100644 (file)
@@ -21,7 +21,7 @@
 
 #include "atl1c.h"
 
-#define ATL1C_DRV_VERSION "1.0.0.2-NAPI"
+#define ATL1C_DRV_VERSION "1.0.1.0-NAPI"
 char atl1c_driver_name[] = "atl1c";
 char atl1c_driver_version[] = ATL1C_DRV_VERSION;
 #define PCI_DEVICE_ID_ATTANSIC_L2C      0x1062
@@ -29,7 +29,7 @@ char atl1c_driver_version[] = ATL1C_DRV_VERSION;
 #define PCI_DEVICE_ID_ATHEROS_L2C_B    0x2060 /* AR8152 v1.1 Fast 10/100 */
 #define PCI_DEVICE_ID_ATHEROS_L2C_B2   0x2062 /* AR8152 v2.0 Fast 10/100 */
 #define PCI_DEVICE_ID_ATHEROS_L1D      0x1073 /* AR8151 v1.0 Gigabit 1000 */
-
+#define PCI_DEVICE_ID_ATHEROS_L1D_2_0  0x1083 /* AR8151 v2.0 Gigabit 1000 */
 #define L2CB_V10                       0xc0
 #define L2CB_V11                       0xc1
 
@@ -97,7 +97,28 @@ static const u16 atl1c_rrd_addr_lo_regs[AT_MAX_RECEIVE_QUEUE] =
 
 static const u32 atl1c_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
        NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP;
+static void atl1c_pcie_patch(struct atl1c_hw *hw)
+{
+       u32 data;
 
+       AT_READ_REG(hw, REG_PCIE_PHYMISC, &data);
+       data |= PCIE_PHYMISC_FORCE_RCV_DET;
+       AT_WRITE_REG(hw, REG_PCIE_PHYMISC, data);
+
+       if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V10) {
+               AT_READ_REG(hw, REG_PCIE_PHYMISC2, &data);
+
+               data &= ~(PCIE_PHYMISC2_SERDES_CDR_MASK <<
+                       PCIE_PHYMISC2_SERDES_CDR_SHIFT);
+               data |= 3 << PCIE_PHYMISC2_SERDES_CDR_SHIFT;
+               data &= ~(PCIE_PHYMISC2_SERDES_TH_MASK <<
+                       PCIE_PHYMISC2_SERDES_TH_SHIFT);
+               data |= 3 << PCIE_PHYMISC2_SERDES_TH_SHIFT;
+               AT_WRITE_REG(hw, REG_PCIE_PHYMISC2, data);
+       }
+}
+
+/* FIXME: no need any more ? */
 /*
  * atl1c_init_pcie - init PCIE module
  */
@@ -127,6 +148,11 @@ static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag)
        data &= ~PCIE_UC_SERVRITY_FCP;
        AT_WRITE_REG(hw, REG_PCIE_UC_SEVERITY, data);
 
+       AT_READ_REG(hw, REG_LTSSM_ID_CTRL, &data);
+       data &= ~LTSSM_ID_EN_WRO;
+       AT_WRITE_REG(hw, REG_LTSSM_ID_CTRL, data);
+
+       atl1c_pcie_patch(hw);
        if (flag & ATL1C_PCIE_L0S_L1_DISABLE)
                atl1c_disable_l0s_l1(hw);
        if (flag & ATL1C_PCIE_PHY_RESET)
@@ -135,7 +161,7 @@ static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag)
                AT_WRITE_REG(hw, REG_GPHY_CTRL,
                        GPHY_CTRL_DEFAULT | GPHY_CTRL_EXT_RESET);
 
-       msleep(1);
+       msleep(5);
 }
 
 /*
@@ -159,6 +185,7 @@ static inline void atl1c_irq_disable(struct atl1c_adapter *adapter)
 {
        atomic_inc(&adapter->irq_sem);
        AT_WRITE_REG(&adapter->hw, REG_IMR, 0);
+       AT_WRITE_REG(&adapter->hw, REG_ISR, ISR_DIS_INT);
        AT_WRITE_FLUSH(&adapter->hw);
        synchronize_irq(adapter->pdev->irq);
 }
@@ -231,15 +258,15 @@ static void atl1c_check_link_status(struct atl1c_adapter *adapter)
 
        if ((phy_data & BMSR_LSTATUS) == 0) {
                /* link down */
-               if (netif_carrier_ok(netdev)) {
-                       hw->hibernate = true;
-                       if (atl1c_stop_mac(hw) != 0)
-                               if (netif_msg_hw(adapter))
-                                       dev_warn(&pdev->dev,
-                                               "stop mac failed\n");
-                       atl1c_set_aspm(hw, false);
-               }
+               hw->hibernate = true;
+               if (atl1c_stop_mac(hw) != 0)
+                       if (netif_msg_hw(adapter))
+                               dev_warn(&pdev->dev, "stop mac failed\n");
+               atl1c_set_aspm(hw, false);
                netif_carrier_off(netdev);
+               netif_stop_queue(netdev);
+               atl1c_phy_reset(hw);
+               atl1c_phy_init(&adapter->hw);
        } else {
                /* Link Up */
                hw->hibernate = false;
@@ -308,6 +335,7 @@ static void atl1c_common_task(struct work_struct *work)
        netdev = adapter->netdev;
 
        if (adapter->work_event & ATL1C_WORK_EVENT_RESET) {
+               adapter->work_event &= ~ATL1C_WORK_EVENT_RESET;
                netif_device_detach(netdev);
                atl1c_down(adapter);
                atl1c_up(adapter);
@@ -315,8 +343,11 @@ static void atl1c_common_task(struct work_struct *work)
                return;
        }
 
-       if (adapter->work_event & ATL1C_WORK_EVENT_LINK_CHANGE)
+       if (adapter->work_event & ATL1C_WORK_EVENT_LINK_CHANGE) {
+               adapter->work_event &= ~ATL1C_WORK_EVENT_LINK_CHANGE;
                atl1c_check_link_status(adapter);
+       }
+       return;
 }
 
 
@@ -476,6 +507,13 @@ static int atl1c_change_mtu(struct net_device *netdev, int new_mtu)
                netdev->mtu = new_mtu;
                adapter->hw.max_frame_size = new_mtu;
                atl1c_set_rxbufsize(adapter, netdev);
+               if (new_mtu > MAX_TSO_FRAME_SIZE) {
+                       adapter->netdev->features &= ~NETIF_F_TSO;
+                       adapter->netdev->features &= ~NETIF_F_TSO6;
+               } else {
+                       adapter->netdev->features |= NETIF_F_TSO;
+                       adapter->netdev->features |= NETIF_F_TSO6;
+               }
                atl1c_down(adapter);
                atl1c_up(adapter);
                clear_bit(__AT_RESETTING, &adapter->flags);
@@ -613,6 +651,9 @@ static void atl1c_set_mac_type(struct atl1c_hw *hw)
        case PCI_DEVICE_ID_ATHEROS_L1D:
                hw->nic_type = athr_l1d;
                break;
+       case PCI_DEVICE_ID_ATHEROS_L1D_2_0:
+               hw->nic_type = athr_l1d_2;
+               break;
        default:
                break;
        }
@@ -627,9 +668,7 @@ static int atl1c_setup_mac_funcs(struct atl1c_hw *hw)
        AT_READ_REG(hw, REG_PHY_STATUS, &phy_status_data);
        AT_READ_REG(hw, REG_LINK_CTRL, &link_ctrl_data);
 
-       hw->ctrl_flags = ATL1C_INTR_CLEAR_ON_READ |
-                        ATL1C_INTR_MODRT_ENABLE  |
-                        ATL1C_RX_IPV6_CHKSUM     |
+       hw->ctrl_flags = ATL1C_INTR_MODRT_ENABLE  |
                         ATL1C_TXQ_MODE_ENHANCE;
        if (link_ctrl_data & LINK_CTRL_L0S_EN)
                hw->ctrl_flags |= ATL1C_ASPM_L0S_SUPPORT;
@@ -637,12 +676,12 @@ static int atl1c_setup_mac_funcs(struct atl1c_hw *hw)
                hw->ctrl_flags |= ATL1C_ASPM_L1_SUPPORT;
        if (link_ctrl_data & LINK_CTRL_EXT_SYNC)
                hw->ctrl_flags |= ATL1C_LINK_EXT_SYNC;
+       hw->ctrl_flags |= ATL1C_ASPM_CTRL_MON;
 
        if (hw->nic_type == athr_l1c ||
-           hw->nic_type == athr_l1d) {
-               hw->ctrl_flags |= ATL1C_ASPM_CTRL_MON;
+           hw->nic_type == athr_l1d ||
+           hw->nic_type == athr_l1d_2)
                hw->link_cap_flags |= ATL1C_LINK_CAP_1000M;
-       }
        return 0;
 }
 /*
@@ -657,6 +696,8 @@ static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter)
 {
        struct atl1c_hw *hw   = &adapter->hw;
        struct pci_dev  *pdev = adapter->pdev;
+       u32 revision;
+
 
        adapter->wol = 0;
        adapter->link_speed = SPEED_0;
@@ -669,7 +710,8 @@ static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter)
        hw->device_id = pdev->device;
        hw->subsystem_vendor_id = pdev->subsystem_vendor;
        hw->subsystem_id = pdev->subsystem_device;
-
+       AT_READ_REG(hw, PCI_CLASS_REVISION, &revision);
+       hw->revision_id = revision & 0xFF;
        /* before link up, we assume hibernate is true */
        hw->hibernate = true;
        hw->media_type = MEDIA_TYPE_AUTO_SENSOR;
@@ -974,6 +1016,7 @@ static void atl1c_configure_des_ring(struct atl1c_adapter *adapter)
        struct atl1c_cmb *cmb = (struct atl1c_cmb *) &adapter->cmb;
        struct atl1c_smb *smb = (struct atl1c_smb *) &adapter->smb;
        int i;
+       u32 data;
 
        /* TPD */
        AT_WRITE_REG(hw, REG_TX_BASE_ADDR_HI,
@@ -1017,6 +1060,23 @@ static void atl1c_configure_des_ring(struct atl1c_adapter *adapter)
                        (u32)((smb->dma & AT_DMA_HI_ADDR_MASK) >> 32));
        AT_WRITE_REG(hw, REG_SMB_BASE_ADDR_LO,
                        (u32)(smb->dma & AT_DMA_LO_ADDR_MASK));
+       if (hw->nic_type == athr_l2c_b) {
+               AT_WRITE_REG(hw, REG_SRAM_RXF_LEN, 0x02a0L);
+               AT_WRITE_REG(hw, REG_SRAM_TXF_LEN, 0x0100L);
+               AT_WRITE_REG(hw, REG_SRAM_RXF_ADDR, 0x029f0000L);
+               AT_WRITE_REG(hw, REG_SRAM_RFD0_INFO, 0x02bf02a0L);
+               AT_WRITE_REG(hw, REG_SRAM_TXF_ADDR, 0x03bf02c0L);
+               AT_WRITE_REG(hw, REG_SRAM_TRD_ADDR, 0x03df03c0L);
+               AT_WRITE_REG(hw, REG_TXF_WATER_MARK, 0);        /* TX watermark, to enter l1 state.*/
+               AT_WRITE_REG(hw, REG_RXD_DMA_CTRL, 0);          /* RXD threshold.*/
+       }
+       if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d_2) {
+                       /* Power Saving for L2c_B */
+               AT_READ_REG(hw, REG_SERDES_LOCK, &data);
+               data |= SERDES_MAC_CLK_SLOWDOWN;
+               data |= SERDES_PYH_CLK_SLOWDOWN;
+               AT_WRITE_REG(hw, REG_SERDES_LOCK, data);
+       }
        /* Load all of base address above */
        AT_WRITE_REG(hw, REG_LOAD_PTR, 1);
 }
@@ -1029,6 +1089,7 @@ static void atl1c_configure_tx(struct atl1c_adapter *adapter)
        u16 tx_offload_thresh;
        u32 txq_ctrl_data;
        u32 extra_size = 0;     /* Jumbo frame threshold in QWORD unit */
+       u32 max_pay_load_data;
 
        extra_size = ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN;
        tx_offload_thresh = MAX_TX_OFFLOAD_THRESH;
@@ -1046,8 +1107,11 @@ static void atl1c_configure_tx(struct atl1c_adapter *adapter)
                        TXQ_NUM_TPD_BURST_SHIFT;
        if (hw->ctrl_flags & ATL1C_TXQ_MODE_ENHANCE)
                txq_ctrl_data |= TXQ_CTRL_ENH_MODE;
-       txq_ctrl_data |= (atl1c_pay_load_size[hw->dmar_block] &
+       max_pay_load_data = (atl1c_pay_load_size[hw->dmar_block] &
                        TXQ_TXF_BURST_NUM_MASK) << TXQ_TXF_BURST_NUM_SHIFT;
+       if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2)
+               max_pay_load_data >>= 1;
+       txq_ctrl_data |= max_pay_load_data;
 
        AT_WRITE_REG(hw, REG_TXQ_CTRL, txq_ctrl_data);
 }
@@ -1078,7 +1142,7 @@ static void atl1c_configure_rx(struct atl1c_adapter *adapter)
        rxq_ctrl_data |= (hw->rss_hash_bits & RSS_HASH_BITS_MASK) <<
                        RSS_HASH_BITS_SHIFT;
        if (hw->ctrl_flags & ATL1C_ASPM_CTRL_MON)
-               rxq_ctrl_data |= (ASPM_THRUPUT_LIMIT_100M &
+               rxq_ctrl_data |= (ASPM_THRUPUT_LIMIT_1M &
                        ASPM_THRUPUT_LIMIT_MASK) << ASPM_THRUPUT_LIMIT_SHIFT;
 
        AT_WRITE_REG(hw, REG_RXQ_CTRL, rxq_ctrl_data);
@@ -1198,21 +1262,23 @@ static int atl1c_reset_mac(struct atl1c_hw *hw)
 {
        struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter;
        struct pci_dev *pdev = adapter->pdev;
-       int ret;
+       u32 master_ctrl_data = 0;
 
        AT_WRITE_REG(hw, REG_IMR, 0);
        AT_WRITE_REG(hw, REG_ISR, ISR_DIS_INT);
 
-       ret = atl1c_stop_mac(hw);
-       if (ret)
-               return ret;
+       atl1c_stop_mac(hw);
        /*
         * Issue Soft Reset to the MAC.  This will reset the chip's
         * transmit, receive, DMA.  It will not effect
         * the current PCI configuration.  The global reset bit is self-
         * clearing, and should clear within a microsecond.
         */
-       AT_WRITE_REGW(hw, REG_MASTER_CTRL, MASTER_CTRL_SOFT_RST);
+       AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data);
+       master_ctrl_data |= MASTER_CTRL_OOB_DIS_OFF;
+       AT_WRITE_REGW(hw, REG_MASTER_CTRL, ((master_ctrl_data | MASTER_CTRL_SOFT_RST)
+                       & 0xFFFF));
+
        AT_WRITE_FLUSH(hw);
        msleep(10);
        /* Wait at least 10ms for All module to be Idle */
@@ -1253,42 +1319,39 @@ static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup)
 {
        u32 pm_ctrl_data;
        u32 link_ctrl_data;
+       u32 link_l1_timer = 0xF;
 
        AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data);
        AT_READ_REG(hw, REG_LINK_CTRL, &link_ctrl_data);
-       pm_ctrl_data &= ~PM_CTRL_SERDES_PD_EX_L1;
 
+       pm_ctrl_data &= ~PM_CTRL_SERDES_PD_EX_L1;
        pm_ctrl_data &=  ~(PM_CTRL_L1_ENTRY_TIMER_MASK <<
                        PM_CTRL_L1_ENTRY_TIMER_SHIFT);
        pm_ctrl_data &= ~(PM_CTRL_LCKDET_TIMER_MASK <<
-                         PM_CTRL_LCKDET_TIMER_SHIFT);
-
-       pm_ctrl_data |= PM_CTRL_MAC_ASPM_CHK;
-       pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
-       pm_ctrl_data |= PM_CTRL_RBER_EN;
-       pm_ctrl_data |= PM_CTRL_SDES_EN;
+                       PM_CTRL_LCKDET_TIMER_SHIFT);
+       pm_ctrl_data |= AT_LCKDET_TIMER << PM_CTRL_LCKDET_TIMER_SHIFT;
 
-       if (hw->nic_type == athr_l2c_b ||
-           hw->nic_type == athr_l1d ||
-           hw->nic_type == athr_l2c_b2) {
+       if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d ||
+               hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) {
                link_ctrl_data &= ~LINK_CTRL_EXT_SYNC;
                if (!(hw->ctrl_flags & ATL1C_APS_MODE_ENABLE)) {
-                       if (hw->nic_type == athr_l2c_b &&
-                           hw->revision_id == L2CB_V10)
+                       if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V10)
                                link_ctrl_data |= LINK_CTRL_EXT_SYNC;
                }
 
                AT_WRITE_REG(hw, REG_LINK_CTRL, link_ctrl_data);
 
-               pm_ctrl_data |= PM_CTRL_PCIE_RECV;
-               pm_ctrl_data |= AT_ASPM_L1_TIMER << PM_CTRL_PM_REQ_TIMER_SHIFT;
-               pm_ctrl_data &= ~PM_CTRL_EN_BUFS_RX_L0S;
+               pm_ctrl_data |= PM_CTRL_RCVR_WT_TIMER;
+               pm_ctrl_data &= ~(PM_CTRL_PM_REQ_TIMER_MASK <<
+                       PM_CTRL_PM_REQ_TIMER_SHIFT);
+               pm_ctrl_data |= AT_ASPM_L1_TIMER <<
+                       PM_CTRL_PM_REQ_TIMER_SHIFT;
                pm_ctrl_data &= ~PM_CTRL_SA_DLY_EN;
                pm_ctrl_data &= ~PM_CTRL_HOTRST;
                pm_ctrl_data |= 1 << PM_CTRL_L1_ENTRY_TIMER_SHIFT;
                pm_ctrl_data |= PM_CTRL_SERDES_PD_EX_L1;
        }
-
+       pm_ctrl_data |= PM_CTRL_MAC_ASPM_CHK;
        if (linkup) {
                pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
                pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
@@ -1297,27 +1360,26 @@ static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup)
                if (hw->ctrl_flags & ATL1C_ASPM_L0S_SUPPORT)
                        pm_ctrl_data |= PM_CTRL_ASPM_L0S_EN;
 
-               if (hw->nic_type == athr_l2c_b ||
-                   hw->nic_type == athr_l1d ||
-                   hw->nic_type == athr_l2c_b2) {
+               if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d ||
+                       hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) {
                        if (hw->nic_type == athr_l2c_b)
                                if (!(hw->ctrl_flags & ATL1C_APS_MODE_ENABLE))
-                                       pm_ctrl_data &= PM_CTRL_ASPM_L0S_EN;
+                                       pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
                        pm_ctrl_data &= ~PM_CTRL_SERDES_L1_EN;
                        pm_ctrl_data &= ~PM_CTRL_SERDES_PLL_L1_EN;
                        pm_ctrl_data &= ~PM_CTRL_SERDES_BUDS_RX_L1_EN;
                        pm_ctrl_data |= PM_CTRL_CLK_SWH_L1;
-                       if (hw->adapter->link_speed == SPEED_100 ||
-                           hw->adapter->link_speed == SPEED_1000) {
-                               pm_ctrl_data &=
-                                       ~(PM_CTRL_L1_ENTRY_TIMER_MASK <<
-                                         PM_CTRL_L1_ENTRY_TIMER_SHIFT);
-                               if (hw->nic_type == athr_l1d)
-                                       pm_ctrl_data |= 0xF <<
-                                               PM_CTRL_L1_ENTRY_TIMER_SHIFT;
-                               else
-                                       pm_ctrl_data |= 7 <<
-                                               PM_CTRL_L1_ENTRY_TIMER_SHIFT;
+               if (hw->adapter->link_speed == SPEED_100 ||
+                               hw->adapter->link_speed == SPEED_1000) {
+                               pm_ctrl_data &=  ~(PM_CTRL_L1_ENTRY_TIMER_MASK <<
+                                       PM_CTRL_L1_ENTRY_TIMER_SHIFT);
+                               if (hw->nic_type == athr_l2c_b)
+                                       link_l1_timer = 7;
+                               else if (hw->nic_type == athr_l2c_b2 ||
+                                       hw->nic_type == athr_l1d_2)
+                                       link_l1_timer = 4;
+                               pm_ctrl_data |= link_l1_timer <<
+                                       PM_CTRL_L1_ENTRY_TIMER_SHIFT;
                        }
                } else {
                        pm_ctrl_data |= PM_CTRL_SERDES_L1_EN;
@@ -1326,24 +1388,12 @@ static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup)
                        pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1;
                        pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
                        pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
-               }
-               atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0x29);
-               if (hw->adapter->link_speed == SPEED_10)
-                       if (hw->nic_type == athr_l1d)
-                               atl1c_write_phy_reg(hw, MII_DBG_ADDR, 0xB69D);
-                       else
-                               atl1c_write_phy_reg(hw, MII_DBG_DATA, 0xB6DD);
-               else if (hw->adapter->link_speed == SPEED_100)
-                       atl1c_write_phy_reg(hw, MII_DBG_DATA, 0xB2DD);
-               else
-                       atl1c_write_phy_reg(hw, MII_DBG_DATA, 0x96DD);
 
+               }
        } else {
-               pm_ctrl_data &= ~PM_CTRL_SERDES_BUDS_RX_L1_EN;
                pm_ctrl_data &= ~PM_CTRL_SERDES_L1_EN;
                pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
                pm_ctrl_data &= ~PM_CTRL_SERDES_PLL_L1_EN;
-
                pm_ctrl_data |= PM_CTRL_CLK_SWH_L1;
 
                if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT)
@@ -1351,8 +1401,9 @@ static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup)
                else
                        pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
        }
-
        AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data);
+
+       return;
 }
 
 static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter)
@@ -1391,7 +1442,8 @@ static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter)
                mac_ctrl_data |= MAC_CTRL_MC_ALL_EN;
 
        mac_ctrl_data |= MAC_CTRL_SINGLE_PAUSE_EN;
-       if (hw->nic_type == athr_l1d || hw->nic_type == athr_l2c_b2) {
+       if (hw->nic_type == athr_l1d || hw->nic_type == athr_l2c_b2 ||
+           hw->nic_type == athr_l1d_2) {
                mac_ctrl_data |= MAC_CTRL_SPEED_MODE_SW;
                mac_ctrl_data |= MAC_CTRL_HASH_ALG_CRC32;
        }
@@ -1409,6 +1461,7 @@ static int atl1c_configure(struct atl1c_adapter *adapter)
        struct atl1c_hw *hw = &adapter->hw;
        u32 master_ctrl_data = 0;
        u32 intr_modrt_data;
+       u32 data;
 
        /* clear interrupt status */
        AT_WRITE_REG(hw, REG_ISR, 0xFFFFFFFF);
@@ -1418,6 +1471,15 @@ static int atl1c_configure(struct atl1c_adapter *adapter)
         * HW will enable self to assert interrupt event to system after
         * waiting x-time for software to notify it accept interrupt.
         */
+
+       data = CLK_GATING_EN_ALL;
+       if (hw->ctrl_flags & ATL1C_CLK_GATING_EN) {
+               if (hw->nic_type == athr_l2c_b)
+                       data &= ~CLK_GATING_RXMAC_EN;
+       } else
+               data = 0;
+       AT_WRITE_REG(hw, REG_CLK_GATING_CTRL, data);
+
        AT_WRITE_REG(hw, REG_INT_RETRIG_TIMER,
                hw->ict & INT_RETRIG_TIMER_MASK);
 
@@ -1436,6 +1498,7 @@ static int atl1c_configure(struct atl1c_adapter *adapter)
        if (hw->ctrl_flags & ATL1C_INTR_CLEAR_ON_READ)
                master_ctrl_data |= MASTER_CTRL_INT_RDCLR;
 
+       master_ctrl_data |= MASTER_CTRL_SA_TIMER_EN;
        AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data);
 
        if (hw->ctrl_flags & ATL1C_CMB_ENABLE) {
@@ -1624,11 +1687,9 @@ static irqreturn_t atl1c_intr(int irq, void *data)
                                        "atl1c hardware error (status = 0x%x)\n",
                                        status & ISR_ERROR);
                        /* reset MAC */
-                       hw->intr_mask &= ~ISR_ERROR;
-                       AT_WRITE_REG(hw, REG_IMR, hw->intr_mask);
                        adapter->work_event |= ATL1C_WORK_EVENT_RESET;
                        schedule_work(&adapter->common_task);
-                       break;
+                       return IRQ_HANDLED;
                }
 
                if (status & ISR_OVER)
@@ -2303,7 +2364,6 @@ void atl1c_down(struct atl1c_adapter *adapter)
        napi_disable(&adapter->napi);
        atl1c_irq_disable(adapter);
        atl1c_free_irq(adapter);
-       AT_WRITE_REG(&adapter->hw, REG_ISR, ISR_DIS_INT);
        /* reset MAC to disable all RX/TX */
        atl1c_reset_mac(&adapter->hw);
        msleep(1);
@@ -2387,79 +2447,68 @@ static int atl1c_suspend(struct pci_dev *pdev, pm_message_t state)
        struct net_device *netdev = pci_get_drvdata(pdev);
        struct atl1c_adapter *adapter = netdev_priv(netdev);
        struct atl1c_hw *hw = &adapter->hw;
-       u32 ctrl;
-       u32 mac_ctrl_data;
-       u32 master_ctrl_data;
+       u32 mac_ctrl_data = 0;
+       u32 master_ctrl_data = 0;
        u32 wol_ctrl_data = 0;
-       u16 mii_bmsr_data;
-       u16 save_autoneg_advertised;
-       u16 mii_intr_status_data;
+       u16 mii_intr_status_data = 0;
        u32 wufc = adapter->wol;
-       u32 i;
        int retval = 0;
 
+       atl1c_disable_l0s_l1(hw);
        if (netif_running(netdev)) {
                WARN_ON(test_bit(__AT_RESETTING, &adapter->flags));
                atl1c_down(adapter);
        }
        netif_device_detach(netdev);
-       atl1c_disable_l0s_l1(hw);
        retval = pci_save_state(pdev);
        if (retval)
                return retval;
+
+       if (wufc)
+               if (atl1c_phy_power_saving(hw) != 0)
+                       dev_dbg(&pdev->dev, "phy power saving failed");
+
+       AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data);
+       AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl_data);
+
+       master_ctrl_data &= ~MASTER_CTRL_CLK_SEL_DIS;
+       mac_ctrl_data &= ~(MAC_CTRL_PRMLEN_MASK << MAC_CTRL_PRMLEN_SHIFT);
+       mac_ctrl_data |= (((u32)adapter->hw.preamble_len &
+                       MAC_CTRL_PRMLEN_MASK) <<
+                       MAC_CTRL_PRMLEN_SHIFT);
+       mac_ctrl_data &= ~(MAC_CTRL_SPEED_MASK << MAC_CTRL_SPEED_SHIFT);
+       mac_ctrl_data &= ~MAC_CTRL_DUPLX;
+
        if (wufc) {
-               AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data);
-               master_ctrl_data &= ~MASTER_CTRL_CLK_SEL_DIS;
-
-               /* get link status */
-               atl1c_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data);
-               atl1c_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data);
-               save_autoneg_advertised = hw->autoneg_advertised;
-               hw->autoneg_advertised = ADVERTISED_10baseT_Half;
-               if (atl1c_restart_autoneg(hw) != 0)
-                       if (netif_msg_link(adapter))
-                               dev_warn(&pdev->dev, "phy autoneg failed\n");
-               hw->phy_configured = false; /* re-init PHY when resume */
-               hw->autoneg_advertised = save_autoneg_advertised;
+               mac_ctrl_data |= MAC_CTRL_RX_EN;
+               if (adapter->link_speed == SPEED_1000 ||
+                       adapter->link_speed == SPEED_0) {
+                       mac_ctrl_data |= atl1c_mac_speed_1000 <<
+                                       MAC_CTRL_SPEED_SHIFT;
+                       mac_ctrl_data |= MAC_CTRL_DUPLX;
+               } else
+                       mac_ctrl_data |= atl1c_mac_speed_10_100 <<
+                                       MAC_CTRL_SPEED_SHIFT;
+
+               if (adapter->link_duplex == DUPLEX_FULL)
+                       mac_ctrl_data |= MAC_CTRL_DUPLX;
+
                /* turn on magic packet wol */
                if (wufc & AT_WUFC_MAG)
-                       wol_ctrl_data = WOL_MAGIC_EN | WOL_MAGIC_PME_EN;
+                       wol_ctrl_data |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN;
 
                if (wufc & AT_WUFC_LNKC) {
-                       for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) {
-                               msleep(100);
-                               atl1c_read_phy_reg(hw, MII_BMSR,
-                                       (u16 *)&mii_bmsr_data);
-                               if (mii_bmsr_data & BMSR_LSTATUS)
-                                       break;
-                       }
-                       if ((mii_bmsr_data & BMSR_LSTATUS) == 0)
-                               if (netif_msg_link(adapter))
-                                       dev_warn(&pdev->dev,
-                                               "%s: Link may change"
-                                               "when suspend\n",
-                                               atl1c_driver_name);
                        wol_ctrl_data |=  WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN;
                        /* only link up can wake up */
                        if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) {
-                               if (netif_msg_link(adapter))
-                                       dev_err(&pdev->dev,
-                                               "%s: read write phy "
-                                               "register failed.\n",
-                                               atl1c_driver_name);
-                               goto wol_dis;
+                               dev_dbg(&pdev->dev, "%s: read write phy "
+                                                 "register failed.\n",
+                                                 atl1c_driver_name);
                        }
                }
                /* clear phy interrupt */
                atl1c_read_phy_reg(hw, MII_ISR, &mii_intr_status_data);
                /* Config MAC Ctrl register */
-               mac_ctrl_data = MAC_CTRL_RX_EN;
-               /* set to 10/100M halt duplex */
-               mac_ctrl_data |= atl1c_mac_speed_10_100 << MAC_CTRL_SPEED_SHIFT;
-               mac_ctrl_data |= (((u32)adapter->hw.preamble_len &
-                                MAC_CTRL_PRMLEN_MASK) <<
-                                MAC_CTRL_PRMLEN_SHIFT);
-
                if (adapter->vlgrp)
                        mac_ctrl_data |= MAC_CTRL_RMV_VLAN;
 
@@ -2467,37 +2516,30 @@ static int atl1c_suspend(struct pci_dev *pdev, pm_message_t state)
                if (wufc & AT_WUFC_MAG)
                        mac_ctrl_data |= MAC_CTRL_BC_EN;
 
-               if (netif_msg_hw(adapter))
-                       dev_dbg(&pdev->dev,
-                               "%s: suspend MAC=0x%x\n",
-                               atl1c_driver_name, mac_ctrl_data);
+               dev_dbg(&pdev->dev,
+                       "%s: suspend MAC=0x%x\n",
+                       atl1c_driver_name, mac_ctrl_data);
                AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data);
                AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl_data);
                AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data);
 
                /* pcie patch */
-               AT_READ_REG(hw, REG_PCIE_PHYMISC, &ctrl);
-               ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
-               AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
+               device_set_wakeup_enable(&pdev->dev, 1);
 
-               pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
-               goto suspend_exit;
+               AT_WRITE_REG(hw, REG_GPHY_CTRL, GPHY_CTRL_DEFAULT |
+                       GPHY_CTRL_EXT_RESET);
+               pci_prepare_to_sleep(pdev);
+       } else {
+               AT_WRITE_REG(hw, REG_GPHY_CTRL, GPHY_CTRL_POWER_SAVING);
+               master_ctrl_data |= MASTER_CTRL_CLK_SEL_DIS;
+               mac_ctrl_data |= atl1c_mac_speed_10_100 << MAC_CTRL_SPEED_SHIFT;
+               mac_ctrl_data |= MAC_CTRL_DUPLX;
+               AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data);
+               AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data);
+               AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
+               hw->phy_configured = false; /* re-init PHY when resume */
+               pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
        }
-wol_dis:
-
-       /* WOL disabled */
-       AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
-
-       /* pcie patch */
-       AT_READ_REG(hw, REG_PCIE_PHYMISC, &ctrl);
-       ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
-       AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
-
-       atl1c_phy_disable(hw);
-       hw->phy_configured = false; /* re-init PHY when resume */
-
-       pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
-suspend_exit:
 
        pci_disable_device(pdev);
        pci_set_power_state(pdev, pci_choose_state(pdev, state));
@@ -2516,9 +2558,19 @@ static int atl1c_resume(struct pci_dev *pdev)
        pci_enable_wake(pdev, PCI_D3cold, 0);
 
        AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0);
+       atl1c_reset_pcie(&adapter->hw, ATL1C_PCIE_L0S_L1_DISABLE |
+                       ATL1C_PCIE_PHY_RESET);
 
        atl1c_phy_reset(&adapter->hw);
        atl1c_reset_mac(&adapter->hw);
+       atl1c_phy_init(&adapter->hw);
+
+#if 0
+       AT_READ_REG(&adapter->hw, REG_PM_CTRLSTAT, &pm_data);
+       pm_data &= ~PM_CTRLSTAT_PME_EN;
+       AT_WRITE_REG(&adapter->hw, REG_PM_CTRLSTAT, pm_data);
+#endif
+
        netif_device_attach(netdev);
        if (netif_running(netdev))
                atl1c_up(adapter);