[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#664461: [squeeze] atl1c: AR8152: "transmit queue 0 timed out" and network is unusable until reset



axst@users.sourceforge.net wrote[1]:

> My latest call to 'aptitude safe-upgrade' installed version #44 replacing my patched
> version #41 − and, unfortunately, I have to report that the bug immediately reappeared
> when I rebooted. So I had to downgrade back to my patched #41, which continues to work
> without problems.

Thanks again for testing.

Debian kernel maintainers: please apply the attached patch, which
applies the following changes from upstream:

 v2.6.36-rc1~571^2~706 atl1c: Add AR8151 v2 support and change L0s/L1 routine
 v2.6.39-rc6~7^2~26    atl1c: Fix work event interrupt/task races
 v2.6.38-rc4~15^2~14   atl1c: Add missing PCI device ID

Axel has been testing the first two since the middle of April.  The
third just adds a new PCI id and there haven't been any relevant
changes upstream since then so I think it should be safe, but I'd
nonetheless prefer to see these changes get a little exposure in
squeeze-proposed-updates before the corresponding point release to be
safe.

Atheros maintainers: after this change, the list of atl1c patches
queued for Debian 6.0r6 on top of 2.6.32.y would be

 496c185c9495 atl1c: Add support for Atheros AR8152 and AR8152
 33ac0b84eeca atl1c: Fix hardware type check for enabling OTP CLK
 8f574b35f22f atl1c: Add AR8151 v2 support and change L0s/L1 routine
 cb771838715b atl1c: Fix work event interrupt/task races
 94dde7e451fa atl1c: Add missing PCI device ID

Is that list missing any important fixes?  If you'd like a git tree,
source tarball, or Debian package to test, feel free to ask.

Thoughts welcome, as usual.  Alas, the patch is too large for the
upstream -stable tree (where new hardware support is not as much of a
priority), so I don't think it would be easy to get help from there.

Hope that helps,
Jonathan

[1] http://bugs.debian.org/664461
Index: debian/patches/series/46
===================================================================
--- debian/patches/series/46	(revision 0)
+++ debian/patches/series/46	(working copy)
@@ -0,0 +1,3 @@
++ features/all/atl1c-Add-AR8151-v2-support-and-change-L0s-L1-routin.patch
++ features/all/atl1c-Fix-work-event-interrupt-task-races.patch
++ features/all/atl1c-Add-missing-PCI-device-ID.patch
Index: debian/patches/features/all/atl1c-Fix-work-event-interrupt-task-races.patch
===================================================================
--- debian/patches/features/all/atl1c-Fix-work-event-interrupt-task-races.patch	(revision 0)
+++ debian/patches/features/all/atl1c-Fix-work-event-interrupt-task-races.patch	(working copy)
@@ -0,0 +1,86 @@
+From: Tim Gardner <timg@tpi.com>
+Date: Wed, 20 Apr 2011 09:00:49 +0000
+Subject: atl1c: Fix work event interrupt/task races
+
+commit cb771838715b1c470bc5735bdae709b33b18e0ad upstream.
+
+The mechanism used to initiate work events from the interrupt
+handler has a classic read/modify/write race between the interrupt
+handler that sets the condition, and the worker task that reads and
+clears the condition. Close these races by using atomic
+bit fields.
+
+Cc: Jie Yang <jie.yang@atheros.com>
+Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ drivers/net/atl1c/atl1c.h      |    6 +++---
+ drivers/net/atl1c/atl1c_main.c |   13 +++++--------
+ 2 files changed, 8 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h
+index 79a693b4352f..a5bbf0c2f2fd 100644
+--- a/drivers/net/atl1c/atl1c.h
++++ b/drivers/net/atl1c/atl1c.h
+@@ -546,9 +546,9 @@ struct atl1c_adapter {
+ #define __AT_TESTING        0x0001
+ #define __AT_RESETTING      0x0002
+ #define __AT_DOWN           0x0003
+-	u8 work_event;
+-#define ATL1C_WORK_EVENT_RESET 		0x01
+-#define ATL1C_WORK_EVENT_LINK_CHANGE	0x02
++	unsigned long work_event;
++#define	ATL1C_WORK_EVENT_RESET		0
++#define	ATL1C_WORK_EVENT_LINK_CHANGE	1
+ 	u32 msg_enable;
+ 
+ 	bool have_msi;
+diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c
+index c102b029d2d8..0f7bf14378c8 100644
+--- a/drivers/net/atl1c/atl1c_main.c
++++ b/drivers/net/atl1c/atl1c_main.c
+@@ -322,7 +322,7 @@ static void atl1c_link_chg_event(struct atl1c_adapter *adapter)
+ 		}
+ 	}
+ 
+-	adapter->work_event |= ATL1C_WORK_EVENT_LINK_CHANGE;
++	set_bit(ATL1C_WORK_EVENT_LINK_CHANGE, &adapter->work_event);
+ 	schedule_work(&adapter->common_task);
+ }
+ 
+@@ -334,19 +334,16 @@ static void atl1c_common_task(struct work_struct *work)
+ 	adapter = container_of(work, struct atl1c_adapter, common_task);
+ 	netdev = adapter->netdev;
+ 
+-	if (adapter->work_event & ATL1C_WORK_EVENT_RESET) {
+-		adapter->work_event &= ~ATL1C_WORK_EVENT_RESET;
++	if (test_and_clear_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event)) {
+ 		netif_device_detach(netdev);
+ 		atl1c_down(adapter);
+ 		atl1c_up(adapter);
+ 		netif_device_attach(netdev);
+-		return;
+ 	}
+ 
+-	if (adapter->work_event & ATL1C_WORK_EVENT_LINK_CHANGE) {
+-		adapter->work_event &= ~ATL1C_WORK_EVENT_LINK_CHANGE;
++	if (test_and_clear_bit(ATL1C_WORK_EVENT_LINK_CHANGE,
++		&adapter->work_event))
+ 		atl1c_check_link_status(adapter);
+-	}
+ }
+ 
+ 
+@@ -365,7 +362,7 @@ static void atl1c_tx_timeout(struct net_device *netdev)
+ 	struct atl1c_adapter *adapter = netdev_priv(netdev);
+ 
+ 	/* Do the reset outside of interrupt context */
+-	adapter->work_event |= ATL1C_WORK_EVENT_RESET;
++	set_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event);
+ 	schedule_work(&adapter->common_task);
+ }
+ 
+-- 
+1.7.10.1
+
Index: debian/patches/features/all/atl1c-Add-missing-PCI-device-ID.patch
===================================================================
--- debian/patches/features/all/atl1c-Add-missing-PCI-device-ID.patch	(revision 0)
+++ debian/patches/features/all/atl1c-Add-missing-PCI-device-ID.patch	(working copy)
@@ -0,0 +1,32 @@
+From: Chuck Ebbert <cebbert@redhat.com>
+Date: Wed, 2 Feb 2011 15:02:08 -0800
+Subject: atl1c: Add missing PCI device ID
+
+commit 94dde7e451fa70749fa68df3d70e4b20debe96a6 upstream.
+
+Commit 8f574b35f22fbb9b5e5f1d11ad6b55b6f35f4533 ("atl1c: Add AR8151 v2
+support and change L0s/L1 routine") added support for a new adapter
+but failed to add it to the PCI device table.
+
+Signed-Off-By: Chuck Ebbert <cebbert@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ drivers/net/atl1c/atl1c_main.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c
+index 0f7bf14378c8..2e57690a6868 100644
+--- a/drivers/net/atl1c/atl1c_main.c
++++ b/drivers/net/atl1c/atl1c_main.c
+@@ -48,6 +48,7 @@ static struct pci_device_id atl1c_pci_tbl[] = {
+ 	{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L2C_B)},
+ 	{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L2C_B2)},
+ 	{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L1D)},
++	{PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATHEROS_L1D_2_0)},
+ 	/* required last entry */
+ 	{ 0 }
+ };
+-- 
+1.7.10.1
+
Index: debian/patches/features/all/atl1c-Add-AR8151-v2-support-and-change-L0s-L1-routin.patch
===================================================================
--- debian/patches/features/all/atl1c-Add-AR8151-v2-support-and-change-L0s-L1-routin.patch	(revision 0)
+++ debian/patches/features/all/atl1c-Add-AR8151-v2-support-and-change-L0s-L1-routin.patch	(working copy)
@@ -0,0 +1,1044 @@
+From: Jie Yang <Jie.Yang@atheros.com>
+Date: Tue, 1 Jun 2010 00:28:12 -0700
+Subject: atl1c: Add AR8151 v2 support and change L0s/L1 routine
+
+commit 8f574b35f22fbb9b5e5f1d11ad6b55b6f35f4533 upstream.
+
+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>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ drivers/net/atl1c/atl1c.h      |    9 +-
+ drivers/net/atl1c/atl1c_hw.c   |  107 ++++++++++--
+ drivers/net/atl1c/atl1c_hw.h   |   49 +++++-
+ drivers/net/atl1c/atl1c_main.c |  355 +++++++++++++++++++++++-----------------
+ 4 files changed, 347 insertions(+), 173 deletions(-)
+
+diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h
+index e00a8f7a673c..79a693b4352f 100644
+--- a/drivers/net/atl1c/atl1c.h
++++ b/drivers/net/atl1c/atl1c.h
+@@ -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
+@@ -87,10 +88,11 @@
+ #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;
+diff --git a/drivers/net/atl1c/atl1c_hw.c b/drivers/net/atl1c/atl1c_hw.c
+index 666d34be0a33..fcc20aa8fb87 100644
+--- a/drivers/net/atl1c/atl1c_hw.c
++++ b/drivers/net/atl1c/atl1c_hw.c
+@@ -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;
+diff --git a/drivers/net/atl1c/atl1c_hw.h b/drivers/net/atl1c/atl1c_hw.h
+index 1eeb3ed9f0cb..3dd675979aa1 100644
+--- a/drivers/net/atl1c/atl1c_hw.h
++++ b/drivers/net/atl1c/atl1c_hw.h
+@@ -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
+diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c
+index 413e5bb7c534..c102b029d2d8 100644
+--- a/drivers/net/atl1c/atl1c_main.c
++++ b/drivers/net/atl1c/atl1c_main.c
+@@ -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,10 +343,10 @@ 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;
++	}
+ }
+ 
+ 
+@@ -478,6 +506,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);
+@@ -615,6 +650,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;
+ 	}
+@@ -629,9 +667,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;
+@@ -639,12 +675,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;
+ }
+ /*
+@@ -659,6 +695,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;
+@@ -671,7 +709,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;
+@@ -965,6 +1004,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,
+@@ -1008,6 +1048,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);
+ }
+@@ -1020,6 +1077,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;
+@@ -1037,8 +1095,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);
+ }
+@@ -1069,7 +1130,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);
+@@ -1189,21 +1250,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 */
+@@ -1244,42 +1307,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_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_LCKDET_TIMER_SHIFT);
++	pm_ctrl_data |= AT_LCKDET_TIMER	<< 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;
+-
+-	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;
+@@ -1288,27 +1348,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;
+@@ -1317,24 +1376,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)
+@@ -1342,8 +1389,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)
+@@ -1382,7 +1430,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;
+ 	}
+@@ -1400,6 +1449,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);
+@@ -1409,6 +1459,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);
+ 
+@@ -1427,6 +1486,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) {
+@@ -1623,11 +1683,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)
+@@ -2296,7 +2354,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);
+@@ -2380,79 +2437,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;
+ 
+-	if (netif_running(netdev)) {
+-		WARN_ON(test_bit(__AT_RESETTING, &adapter->flags));
+-		atl1c_down(adapter);
+-	}
+-	netif_device_detach(netdev);
+ 	atl1c_disable_l0s_l1(hw);
++	if (netif_running(netdev)) {
++		WARN_ON(test_bit(__AT_RESETTING, &adapter->flags));
++		atl1c_down(adapter);
++	}
++	netif_device_detach(netdev);
+ 	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;
++		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;
+ 
+-		/* 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;
+ 		/* 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;
+ 
+@@ -2460,37 +2506,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));
+@@ -2509,9 +2548,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);
+-- 
+1.7.10.1
+
Index: debian/changelog
===================================================================
--- debian/changelog	(revision 18988)
+++ debian/changelog	(working copy)
@@ -1,7 +1,14 @@
 linux-2.6 (2.6.32-46) UNRELEASED; urgency=low
 
+  [ Bastian Blank ]
   * [s390] Enable IUCV special message support. (closes: #671238)
 
+  [ Jonathan Nieder ]
+  * atl1c: Fix support for Atheros AR8152 v2 (Closes: #664461)
+    - Add AR8151 v2 support and change L0s/L1 routine
+    - Fix work event interrupt/task races
+    - Add missing PCI device ID
+
  -- Bastian Blank <waldi@debian.org>  Mon, 07 May 2012 19:18:05 +0200
 
 linux-2.6 (2.6.32-45) stable; urgency=high

Reply to: