atl1c: fix WoL(magic) issue for l2cb 1.1
[cascardo/linux.git] / drivers / net / ethernet / atheros / atl1c / atl1c_main.c
index 12b4725..e671367 100644 (file)
@@ -64,7 +64,7 @@ static int atl1c_stop_mac(struct atl1c_hw *hw);
 static void atl1c_enable_rx_ctrl(struct atl1c_hw *hw);
 static void atl1c_enable_tx_ctrl(struct atl1c_hw *hw);
 static void atl1c_disable_l0s_l1(struct atl1c_hw *hw);
-static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup);
+static void atl1c_set_aspm(struct atl1c_hw *hw, u16 link_speed);
 static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter);
 static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter,
                   int *work_done, int work_to_do);
@@ -80,22 +80,44 @@ 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;
+       u32 mst_data, data;
 
-       AT_READ_REG(hw, REG_PCIE_PHYMISC, &data);
-       data |= PCIE_PHYMISC_FORCE_RCV_DET;
-       AT_WRITE_REG(hw, REG_PCIE_PHYMISC, data);
+       /* pclk sel could switch to 25M */
+       AT_READ_REG(hw, REG_MASTER_CTRL, &mst_data);
+       mst_data &= ~MASTER_CTRL_CLK_SEL_DIS;
+       AT_WRITE_REG(hw, REG_MASTER_CTRL, mst_data);
 
+       /* WoL/PCIE related settings */
+       if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) {
+               AT_READ_REG(hw, REG_PCIE_PHYMISC, &data);
+               data |= PCIE_PHYMISC_FORCE_RCV_DET;
+               AT_WRITE_REG(hw, REG_PCIE_PHYMISC, data);
+       } else { /* new dev set bit5 of MASTER */
+               if (!(mst_data & MASTER_CTRL_WAKEN_25M))
+                       AT_WRITE_REG(hw, REG_MASTER_CTRL,
+                               mst_data | MASTER_CTRL_WAKEN_25M);
+       }
+       /* aspm/PCIE setting only for l2cb 1.0 */
        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;
+               data = FIELD_SETX(data, PCIE_PHYMISC2_CDR_BW,
+                       L2CB1_PCIE_PHYMISC2_CDR_BW);
+               data = FIELD_SETX(data, PCIE_PHYMISC2_L0S_TH,
+                       L2CB1_PCIE_PHYMISC2_L0S_TH);
                AT_WRITE_REG(hw, REG_PCIE_PHYMISC2, data);
+               /* extend L1 sync timer */
+               AT_READ_REG(hw, REG_LINK_CTRL, &data);
+               data |= LINK_CTRL_EXT_SYNC;
+               AT_WRITE_REG(hw, REG_LINK_CTRL, data);
+       }
+       /* l2cb 1.x & l1d 1.x */
+       if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l1d) {
+               AT_READ_REG(hw, REG_PM_CTRL, &data);
+               data |= PM_CTRL_L0S_BUFSRX_EN;
+               AT_WRITE_REG(hw, REG_PM_CTRL, data);
+               /* clear vendor msg */
+               AT_READ_REG(hw, REG_DMA_DBG, &data);
+               AT_WRITE_REG(hw, REG_DMA_DBG, data & ~DMA_DBG_VENDOR_MSG);
        }
 }
 
@@ -108,6 +130,7 @@ static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag)
        u32 data;
        u32 pci_cmd;
        struct pci_dev *pdev = hw->adapter->pdev;
+       int pos;
 
        AT_READ_REG(hw, PCI_COMMAND, &pci_cmd);
        pci_cmd &= ~PCI_COMMAND_INTX_DISABLE;
@@ -124,10 +147,16 @@ static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag)
        /*
         * Mask some pcie error bits
         */
-       AT_READ_REG(hw, REG_PCIE_UC_SEVERITY, &data);
-       data &= ~PCIE_UC_SERVRITY_DLP;
-       data &= ~PCIE_UC_SERVRITY_FCP;
-       AT_WRITE_REG(hw, REG_PCIE_UC_SEVERITY, data);
+       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
+       pci_read_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, &data);
+       data &= ~(PCI_ERR_UNC_DLP | PCI_ERR_UNC_FCP);
+       pci_write_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, data);
+       /* clear error status */
+       pci_write_config_word(pdev, pci_pcie_cap(pdev) + PCI_EXP_DEVSTA,
+                       PCI_EXP_DEVSTA_NFED |
+                       PCI_EXP_DEVSTA_FED |
+                       PCI_EXP_DEVSTA_CED |
+                       PCI_EXP_DEVSTA_URD);
 
        AT_READ_REG(hw, REG_LTSSM_ID_CTRL, &data);
        data &= ~LTSSM_ID_EN_WRO;
@@ -243,7 +272,7 @@ static void atl1c_check_link_status(struct atl1c_adapter *adapter)
                if (atl1c_stop_mac(hw) != 0)
                        if (netif_msg_hw(adapter))
                                dev_warn(&pdev->dev, "stop mac failed\n");
-               atl1c_set_aspm(hw, false);
+               atl1c_set_aspm(hw, SPEED_0);
                netif_carrier_off(netdev);
                netif_stop_queue(netdev);
                atl1c_phy_reset(hw);
@@ -261,7 +290,7 @@ static void atl1c_check_link_status(struct atl1c_adapter *adapter)
                    adapter->link_duplex != duplex) {
                        adapter->link_speed  = speed;
                        adapter->link_duplex = duplex;
-                       atl1c_set_aspm(hw, true);
+                       atl1c_set_aspm(hw, speed);
                        atl1c_enable_tx_ctrl(hw);
                        atl1c_enable_rx_ctrl(hw);
                        atl1c_setup_mac_ctrl(adapter);
@@ -679,12 +708,8 @@ static int atl1c_setup_mac_funcs(struct atl1c_hw *hw)
 
        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;
-       if (link_ctrl_data & LINK_CTRL_L1_EN)
-               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_L0S_SUPPORT |
+                         ATL1C_ASPM_L1_SUPPORT;
        hw->ctrl_flags |= ATL1C_ASPM_CTRL_MON;
 
        if (hw->nic_type == athr_l1c ||
@@ -1172,9 +1197,9 @@ static int atl1c_reset_mac(struct atl1c_hw *hw)
         * clearing, and should clear within a microsecond.
         */
        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));
+       master_ctrl_data |= MASTER_CTRL_OOB_DIS;
+       AT_WRITE_REG(hw, REG_MASTER_CTRL,
+               master_ctrl_data | MASTER_CTRL_SOFT_RST);
 
        AT_WRITE_FLUSH(hw);
        msleep(10);
@@ -1186,117 +1211,90 @@ static int atl1c_reset_mac(struct atl1c_hw *hw)
                        " disabled for 10ms second\n");
                return -1;
        }
+       AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl_data);
+
        return 0;
 }
 
 static void atl1c_disable_l0s_l1(struct atl1c_hw *hw)
 {
-       u32 pm_ctrl_data;
+       u16 ctrl_flags = hw->ctrl_flags;
 
-       AT_READ_REG(hw, REG_PM_CTRL, &pm_ctrl_data);
-       pm_ctrl_data &= ~(PM_CTRL_L1_ENTRY_TIMER_MASK <<
-                       PM_CTRL_L1_ENTRY_TIMER_SHIFT);
-       pm_ctrl_data &= ~PM_CTRL_CLK_SWH_L1;
-       pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
-       pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
-       pm_ctrl_data &= ~PM_CTRL_MAC_ASPM_CHK;
-       pm_ctrl_data &= ~PM_CTRL_SERDES_PD_EX_L1;
-
-       pm_ctrl_data |= PM_CTRL_SERDES_BUDS_RX_L1_EN;
-       pm_ctrl_data |= PM_CTRL_SERDES_PLL_L1_EN;
-       pm_ctrl_data |= PM_CTRL_SERDES_L1_EN;
-       AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data);
+       hw->ctrl_flags &= ~(ATL1C_ASPM_L0S_SUPPORT | ATL1C_ASPM_L1_SUPPORT);
+       atl1c_set_aspm(hw, SPEED_0);
+       hw->ctrl_flags = ctrl_flags;
 }
 
 /*
  * Set ASPM state.
  * Enable/disable L0s/L1 depend on link state.
  */
-static void atl1c_set_aspm(struct atl1c_hw *hw, bool linkup)
+static void atl1c_set_aspm(struct atl1c_hw *hw, u16 link_speed)
 {
        u32 pm_ctrl_data;
-       u32 link_ctrl_data;
-       u32 link_l1_timer = 0xF;
+       u32 link_l1_timer;
 
        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_ASPM_L1_EN |
+                         PM_CTRL_ASPM_L0S_EN |
+                         PM_CTRL_MAC_ASPM_CHK);
+       /* L1 timer */
+       if (hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) {
+               pm_ctrl_data &= ~PMCTRL_TXL1_AFTER_L0S;
+               link_l1_timer =
+                       link_speed == SPEED_1000 || link_speed == SPEED_100 ?
+                       L1D_PMCTRL_L1_ENTRY_TM_16US : 1;
+               pm_ctrl_data = FIELD_SETX(pm_ctrl_data,
+                       L1D_PMCTRL_L1_ENTRY_TM, link_l1_timer);
+       } else {
+               link_l1_timer = hw->nic_type == athr_l2c_b ?
+                       L2CB1_PM_CTRL_L1_ENTRY_TM : L1C_PM_CTRL_L1_ENTRY_TM;
+               if (link_speed != SPEED_1000 && link_speed != SPEED_100)
+                       link_l1_timer = 1;
+               pm_ctrl_data = FIELD_SETX(pm_ctrl_data,
+                       PM_CTRL_L1_ENTRY_TIMER, link_l1_timer);
+       }
 
-       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 |= AT_LCKDET_TIMER << PM_CTRL_LCKDET_TIMER_SHIFT;
+       /* L0S/L1 enable */
+       if (hw->ctrl_flags & ATL1C_ASPM_L0S_SUPPORT)
+               pm_ctrl_data |= PM_CTRL_ASPM_L0S_EN | PM_CTRL_MAC_ASPM_CHK;
+       if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT)
+               pm_ctrl_data |= PM_CTRL_ASPM_L1_EN | PM_CTRL_MAC_ASPM_CHK;
 
+       /* l2cb & l1d & l2cb2 & l1d2 */
        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)
-                               link_ctrl_data |= LINK_CTRL_EXT_SYNC;
-               }
-
-               AT_WRITE_REG(hw, REG_LINK_CTRL, link_ctrl_data);
-
-               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;
-               if (hw->ctrl_flags & ATL1C_ASPM_L1_SUPPORT)
-                       pm_ctrl_data |= PM_CTRL_ASPM_L1_EN;
-               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 || 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_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_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;
-                       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;
+           hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) {
+               pm_ctrl_data = FIELD_SETX(pm_ctrl_data,
+                       PM_CTRL_PM_REQ_TIMER, PM_CTRL_PM_REQ_TO_DEF);
+               pm_ctrl_data |= PM_CTRL_RCVR_WT_TIMER |
+                               PM_CTRL_SERDES_PD_EX_L1 |
+                               PM_CTRL_CLK_SWH_L1;
+               pm_ctrl_data &= ~(PM_CTRL_SERDES_L1_EN |
+                                 PM_CTRL_SERDES_PLL_L1_EN |
+                                 PM_CTRL_SERDES_BUFS_RX_L1_EN |
+                                 PM_CTRL_SA_DLY_EN |
+                                 PM_CTRL_HOTRST);
+               /* disable l0s if link down or l2cb */
+               if (link_speed == SPEED_0 || hw->nic_type == athr_l2c_b)
                        pm_ctrl_data &= ~PM_CTRL_ASPM_L0S_EN;
-                       pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
-
+       } else { /* l1c */
+               pm_ctrl_data =
+                       FIELD_SETX(pm_ctrl_data, PM_CTRL_L1_ENTRY_TIMER, 0);
+               if (link_speed != SPEED_0) {
+                       pm_ctrl_data |= PM_CTRL_SERDES_L1_EN |
+                                       PM_CTRL_SERDES_PLL_L1_EN |
+                                       PM_CTRL_SERDES_BUFS_RX_L1_EN;
+                       pm_ctrl_data &= ~(PM_CTRL_SERDES_PD_EX_L1 |
+                                         PM_CTRL_CLK_SWH_L1 |
+                                         PM_CTRL_ASPM_L0S_EN |
+                                         PM_CTRL_ASPM_L1_EN);
+               } else { /* link down */
+                       pm_ctrl_data |= PM_CTRL_CLK_SWH_L1;
+                       pm_ctrl_data &= ~(PM_CTRL_SERDES_L1_EN |
+                                         PM_CTRL_SERDES_PLL_L1_EN |
+                                         PM_CTRL_SERDES_BUFS_RX_L1_EN |
+                                         PM_CTRL_ASPM_L0S_EN);
                }
-       } else {
-               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)
-                       pm_ctrl_data |= PM_CTRL_ASPM_L1_EN;
-               else
-                       pm_ctrl_data &= ~PM_CTRL_ASPM_L1_EN;
        }
        AT_WRITE_REG(hw, REG_PM_CTRL, pm_ctrl_data);
 
@@ -1359,6 +1357,10 @@ static int atl1c_configure(struct atl1c_adapter *adapter)
        u32 intr_modrt_data;
        u32 data;
 
+       AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl_data);
+       master_ctrl_data &= ~(MASTER_CTRL_TX_ITIMER_EN |
+                             MASTER_CTRL_RX_ITIMER_EN |
+                             MASTER_CTRL_INT_RDCLR);
        /* clear interrupt status */
        AT_WRITE_REG(hw, REG_ISR, 0xFFFFFFFF);
        /*  Clear any WOL status */
@@ -2223,6 +2225,8 @@ static void atl1c_down(struct atl1c_adapter *adapter)
        napi_disable(&adapter->napi);
        atl1c_irq_disable(adapter);
        atl1c_free_irq(adapter);
+       /* disable ASPM if device inactive */
+       atl1c_disable_l0s_l1(&adapter->hw);
        /* reset MAC to disable all RX/TX */
        atl1c_reset_mac(&adapter->hw);
        msleep(1);
@@ -2350,9 +2354,14 @@ static int atl1c_suspend(struct device *dev)
                        mac_ctrl_data |= MAC_CTRL_DUPLX;
 
                /* turn on magic packet wol */
-               if (wufc & AT_WUFC_MAG)
+               if (wufc & AT_WUFC_MAG) {
                        wol_ctrl_data |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN;
-
+                       if (hw->nic_type == athr_l2c_b &&
+                           hw->revision_id == L2CB_V11) {
+                               wol_ctrl_data |=
+                                       WOL_PATTERN_EN | WOL_PATTERN_PME_EN;
+                       }
+               }
                if (wufc & AT_WUFC_LNKC) {
                        wol_ctrl_data |=  WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN;
                        /* only link up can wake up */