summaryrefslogtreecommitdiff
path: root/target/linux
diff options
context:
space:
mode:
authorjuhosg <juhosg@3c298f89-4303-0410-b956-a3cf2f4a3e73>2012-11-29 17:58:28 +0000
committerjuhosg <juhosg@3c298f89-4303-0410-b956-a3cf2f4a3e73>2012-11-29 17:58:28 +0000
commit97b2abe1a3720cb7461fecbaf02690c74beaa26b (patch)
treed9fa276ed6c56a8388d853ade15b62ebf22ace1f /target/linux
parent4e3096c90dc8e86c04a16392d5da61262c5b0281 (diff)
kernel: Fix 8139cp ring buffer initialisation, tx timeout recovery, add BQL
Patches queued upstream in net-next.git Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> Signed-off-by: Gabor Juhos <juhosg@openwrt.org> git-svn-id: svn://svn.openwrt.org/openwrt/trunk@34415 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux')
-rw-r--r--target/linux/generic/patches-3.3/065-8139cp-fixes.patch189
-rw-r--r--target/linux/generic/patches-3.6/065-8139cp-fixes.patch176
-rw-r--r--target/linux/generic/patches-3.7/065-8139cp-fixes.patch189
3 files changed, 554 insertions, 0 deletions
diff --git a/target/linux/generic/patches-3.3/065-8139cp-fixes.patch b/target/linux/generic/patches-3.3/065-8139cp-fixes.patch
new file mode 100644
index 0000000000..33ca1a128a
--- /dev/null
+++ b/target/linux/generic/patches-3.3/065-8139cp-fixes.patch
@@ -0,0 +1,189 @@
+commit 01ffc0a7f1c1801a2354719dedbc32aff45b987d
+Author: David Woodhouse <dwmw2@infradead.org>
+Date: Sat Nov 24 12:11:21 2012 +0000
+
+ 8139cp: re-enable interrupts after tx timeout
+
+ Recovery doesn't work too well if we leave interrupts disabled...
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Acked-by: Francois Romieu <romieu@fr.zoreil.com>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 871f0d4c153e1258d4becf306eca6761bf38b629
+Author: David Woodhouse <dwmw2@infradead.org>
+Date: Thu Nov 22 03:16:58 2012 +0000
+
+ 8139cp: enable bql
+
+ This adds support for byte queue limits on RTL8139C+
+
+ Tested on real hardware.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Acked-By: Dave Täht <dave.taht@bufferbloat.net>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit a9dbe40fc10cea2efe6e1ff9e03c62dd7579c5ba
+Author: David Woodhouse <dwmw2@infradead.org>
+Date: Wed Nov 21 10:27:19 2012 +0000
+
+ 8139cp: set ring address after enabling C+ mode
+
+ This fixes (for me) a regression introduced by commit b01af457 ("8139cp:
+ set ring address before enabling receiver"). That commit configured the
+ descriptor ring addresses earlier in the initialisation sequence, in
+ order to avoid the possibility of triggering stray DMA before the
+ correct address had been set up.
+
+ Unfortunately, it seems that the hardware will scribble garbage into the
+ TxRingAddr registers when we enable "plus mode" Tx in the CpCmd
+ register. Observed on a Traverse Geos router board.
+
+ To deal with this, while not reintroducing the problem which led to the
+ original commit, we augment cp_start_hw() to write to the CpCmd register
+ *first*, then set the descriptor ring addresses, and then finally to
+ enable Rx and Tx in the original 8139 Cmd register. The datasheet
+ actually indicates that we should enable Tx/Rx in the Cmd register
+ *before* configuring the descriptor addresses, but that would appear to
+ re-introduce the problem that the offending commit b01af457 was trying
+ to solve. And this variant appears to work fine on real hardware.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Cc: stable@kernel.org [3.5+]
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 071e3ef4a94a021b16a2912f3885c86f4ff36b49
+Author: David S. Miller <davem@davemloft.net>
+Date: Sun Nov 25 15:52:09 2012 -0500
+
+ Revert "8139cp: revert "set ring address before enabling receiver""
+
+ This reverts commit b26623dab7eeb1e9f5898c7a49458789dd492f20.
+
+ This reverts the revert, in net-next we'll try another scheme
+ to fix this bug using patches from David Woodhouse.
+
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c
+index b01f83a..6cb96b4 100644
+--- a/drivers/net/ethernet/realtek/8139cp.c
++++ b/drivers/net/ethernet/realtek/8139cp.c
+@@ -648,6 +648,7 @@ static void cp_tx (struct cp_private *cp)
+ {
+ unsigned tx_head = cp->tx_head;
+ unsigned tx_tail = cp->tx_tail;
++ unsigned bytes_compl = 0, pkts_compl = 0;
+
+ while (tx_tail != tx_head) {
+ struct cp_desc *txd = cp->tx_ring + tx_tail;
+@@ -666,6 +667,9 @@ static void cp_tx (struct cp_private *cp)
+ le32_to_cpu(txd->opts1) & 0xffff,
+ PCI_DMA_TODEVICE);
+
++ bytes_compl += skb->len;
++ pkts_compl++;
++
+ if (status & LastFrag) {
+ if (status & (TxError | TxFIFOUnder)) {
+ netif_dbg(cp, tx_err, cp->dev,
+@@ -697,6 +701,7 @@ static void cp_tx (struct cp_private *cp)
+
+ cp->tx_tail = tx_tail;
+
++ netdev_completed_queue(cp->dev, pkts_compl, bytes_compl);
+ if (TX_BUFFS_AVAIL(cp) > (MAX_SKB_FRAGS + 1))
+ netif_wake_queue(cp->dev);
+ }
+@@ -843,6 +848,8 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
+ wmb();
+ }
+ cp->tx_head = entry;
++
++ netdev_sent_queue(dev, skb->len);
+ netif_dbg(cp, tx_queued, cp->dev, "tx queued, slot %d, skblen %d\n",
+ entry, skb->len);
+ if (TX_BUFFS_AVAIL(cp) <= (MAX_SKB_FRAGS + 1))
+@@ -937,6 +944,8 @@ static void cp_stop_hw (struct cp_private *cp)
+
+ cp->rx_tail = 0;
+ cp->tx_head = cp->tx_tail = 0;
++
++ netdev_reset_queue(cp->dev);
+ }
+
+ static void cp_reset_hw (struct cp_private *cp)
+@@ -957,8 +966,38 @@ static void cp_reset_hw (struct cp_private *cp)
+
+ static inline void cp_start_hw (struct cp_private *cp)
+ {
++ dma_addr_t ring_dma;
++
+ cpw16(CpCmd, cp->cpcmd);
++
++ /*
++ * These (at least TxRingAddr) need to be configured after the
++ * corresponding bits in CpCmd are enabled. Datasheet v1.6 §6.33
++ * (C+ Command Register) recommends that these and more be configured
++ * *after* the [RT]xEnable bits in CpCmd are set. And on some hardware
++ * it's been observed that the TxRingAddr is actually reset to garbage
++ * when C+ mode Tx is enabled in CpCmd.
++ */
++ cpw32_f(HiTxRingAddr, 0);
++ cpw32_f(HiTxRingAddr + 4, 0);
++
++ ring_dma = cp->ring_dma;
++ cpw32_f(RxRingAddr, ring_dma & 0xffffffff);
++ cpw32_f(RxRingAddr + 4, (ring_dma >> 16) >> 16);
++
++ ring_dma += sizeof(struct cp_desc) * CP_RX_RING_SIZE;
++ cpw32_f(TxRingAddr, ring_dma & 0xffffffff);
++ cpw32_f(TxRingAddr + 4, (ring_dma >> 16) >> 16);
++
++ /*
++ * Strictly speaking, the datasheet says this should be enabled
++ * *before* setting the descriptor addresses. But what, then, would
++ * prevent it from doing DMA to random unconfigured addresses?
++ * This variant appears to work fine.
++ */
+ cpw8(Cmd, RxOn | TxOn);
++
++ netdev_reset_queue(cp->dev);
+ }
+
+ static void cp_enable_irq(struct cp_private *cp)
+@@ -969,7 +1008,6 @@ static void cp_enable_irq(struct cp_private *cp)
+ static void cp_init_hw (struct cp_private *cp)
+ {
+ struct net_device *dev = cp->dev;
+- dma_addr_t ring_dma;
+
+ cp_reset_hw(cp);
+
+@@ -992,17 +1030,6 @@ static void cp_init_hw (struct cp_private *cp)
+
+ cpw8(Config5, cpr8(Config5) & PMEStatus);
+
+- cpw32_f(HiTxRingAddr, 0);
+- cpw32_f(HiTxRingAddr + 4, 0);
+-
+- ring_dma = cp->ring_dma;
+- cpw32_f(RxRingAddr, ring_dma & 0xffffffff);
+- cpw32_f(RxRingAddr + 4, (ring_dma >> 16) >> 16);
+-
+- ring_dma += sizeof(struct cp_desc) * CP_RX_RING_SIZE;
+- cpw32_f(TxRingAddr, ring_dma & 0xffffffff);
+- cpw32_f(TxRingAddr + 4, (ring_dma >> 16) >> 16);
+-
+ cpw16(MultiIntr, 0);
+
+ cpw8_f(Cfg9346, Cfg9346_Lock);
+@@ -1192,6 +1219,7 @@ static void cp_tx_timeout(struct net_device *dev)
+ cp_clean_rings(cp);
+ rc = cp_init_rings(cp);
+ cp_start_hw(cp);
++ cp_enable_irq(cp);
+
+ netif_wake_queue(dev);
+
diff --git a/target/linux/generic/patches-3.6/065-8139cp-fixes.patch b/target/linux/generic/patches-3.6/065-8139cp-fixes.patch
new file mode 100644
index 0000000000..f0087bfee3
--- /dev/null
+++ b/target/linux/generic/patches-3.6/065-8139cp-fixes.patch
@@ -0,0 +1,176 @@
+commit 01ffc0a7f1c1801a2354719dedbc32aff45b987d
+Author: David Woodhouse <dwmw2@infradead.org>
+Date: Sat Nov 24 12:11:21 2012 +0000
+
+ 8139cp: re-enable interrupts after tx timeout
+
+ Recovery doesn't work too well if we leave interrupts disabled...
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Acked-by: Francois Romieu <romieu@fr.zoreil.com>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 871f0d4c153e1258d4becf306eca6761bf38b629
+Author: David Woodhouse <dwmw2@infradead.org>
+Date: Thu Nov 22 03:16:58 2012 +0000
+
+ 8139cp: enable bql
+
+ This adds support for byte queue limits on RTL8139C+
+
+ Tested on real hardware.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Acked-By: Dave Täht <dave.taht@bufferbloat.net>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit a9dbe40fc10cea2efe6e1ff9e03c62dd7579c5ba
+Author: David Woodhouse <dwmw2@infradead.org>
+Date: Wed Nov 21 10:27:19 2012 +0000
+
+ 8139cp: set ring address after enabling C+ mode
+
+ This fixes (for me) a regression introduced by commit b01af457 ("8139cp:
+ set ring address before enabling receiver"). That commit configured the
+ descriptor ring addresses earlier in the initialisation sequence, in
+ order to avoid the possibility of triggering stray DMA before the
+ correct address had been set up.
+
+ Unfortunately, it seems that the hardware will scribble garbage into the
+ TxRingAddr registers when we enable "plus mode" Tx in the CpCmd
+ register. Observed on a Traverse Geos router board.
+
+ To deal with this, while not reintroducing the problem which led to the
+ original commit, we augment cp_start_hw() to write to the CpCmd register
+ *first*, then set the descriptor ring addresses, and then finally to
+ enable Rx and Tx in the original 8139 Cmd register. The datasheet
+ actually indicates that we should enable Tx/Rx in the Cmd register
+ *before* configuring the descriptor addresses, but that would appear to
+ re-introduce the problem that the offending commit b01af457 was trying
+ to solve. And this variant appears to work fine on real hardware.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Cc: stable@kernel.org [3.5+]
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c
+index 1c81825..6cb96b4 100644
+--- a/drivers/net/ethernet/realtek/8139cp.c
++++ b/drivers/net/ethernet/realtek/8139cp.c
+@@ -648,6 +648,7 @@ static void cp_tx (struct cp_private *cp)
+ {
+ unsigned tx_head = cp->tx_head;
+ unsigned tx_tail = cp->tx_tail;
++ unsigned bytes_compl = 0, pkts_compl = 0;
+
+ while (tx_tail != tx_head) {
+ struct cp_desc *txd = cp->tx_ring + tx_tail;
+@@ -666,6 +667,9 @@ static void cp_tx (struct cp_private *cp)
+ le32_to_cpu(txd->opts1) & 0xffff,
+ PCI_DMA_TODEVICE);
+
++ bytes_compl += skb->len;
++ pkts_compl++;
++
+ if (status & LastFrag) {
+ if (status & (TxError | TxFIFOUnder)) {
+ netif_dbg(cp, tx_err, cp->dev,
+@@ -697,6 +701,7 @@ static void cp_tx (struct cp_private *cp)
+
+ cp->tx_tail = tx_tail;
+
++ netdev_completed_queue(cp->dev, pkts_compl, bytes_compl);
+ if (TX_BUFFS_AVAIL(cp) > (MAX_SKB_FRAGS + 1))
+ netif_wake_queue(cp->dev);
+ }
+@@ -843,6 +848,8 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
+ wmb();
+ }
+ cp->tx_head = entry;
++
++ netdev_sent_queue(dev, skb->len);
+ netif_dbg(cp, tx_queued, cp->dev, "tx queued, slot %d, skblen %d\n",
+ entry, skb->len);
+ if (TX_BUFFS_AVAIL(cp) <= (MAX_SKB_FRAGS + 1))
+@@ -937,6 +944,8 @@ static void cp_stop_hw (struct cp_private *cp)
+
+ cp->rx_tail = 0;
+ cp->tx_head = cp->tx_tail = 0;
++
++ netdev_reset_queue(cp->dev);
+ }
+
+ static void cp_reset_hw (struct cp_private *cp)
+@@ -957,8 +966,38 @@ static void cp_reset_hw (struct cp_private *cp)
+
+ static inline void cp_start_hw (struct cp_private *cp)
+ {
++ dma_addr_t ring_dma;
++
+ cpw16(CpCmd, cp->cpcmd);
++
++ /*
++ * These (at least TxRingAddr) need to be configured after the
++ * corresponding bits in CpCmd are enabled. Datasheet v1.6 §6.33
++ * (C+ Command Register) recommends that these and more be configured
++ * *after* the [RT]xEnable bits in CpCmd are set. And on some hardware
++ * it's been observed that the TxRingAddr is actually reset to garbage
++ * when C+ mode Tx is enabled in CpCmd.
++ */
++ cpw32_f(HiTxRingAddr, 0);
++ cpw32_f(HiTxRingAddr + 4, 0);
++
++ ring_dma = cp->ring_dma;
++ cpw32_f(RxRingAddr, ring_dma & 0xffffffff);
++ cpw32_f(RxRingAddr + 4, (ring_dma >> 16) >> 16);
++
++ ring_dma += sizeof(struct cp_desc) * CP_RX_RING_SIZE;
++ cpw32_f(TxRingAddr, ring_dma & 0xffffffff);
++ cpw32_f(TxRingAddr + 4, (ring_dma >> 16) >> 16);
++
++ /*
++ * Strictly speaking, the datasheet says this should be enabled
++ * *before* setting the descriptor addresses. But what, then, would
++ * prevent it from doing DMA to random unconfigured addresses?
++ * This variant appears to work fine.
++ */
+ cpw8(Cmd, RxOn | TxOn);
++
++ netdev_reset_queue(cp->dev);
+ }
+
+ static void cp_enable_irq(struct cp_private *cp)
+@@ -969,7 +1008,6 @@ static void cp_enable_irq(struct cp_private *cp)
+ static void cp_init_hw (struct cp_private *cp)
+ {
+ struct net_device *dev = cp->dev;
+- dma_addr_t ring_dma;
+
+ cp_reset_hw(cp);
+
+@@ -979,17 +1017,6 @@ static void cp_init_hw (struct cp_private *cp)
+ cpw32_f (MAC0 + 0, le32_to_cpu (*(__le32 *) (dev->dev_addr + 0)));
+ cpw32_f (MAC0 + 4, le32_to_cpu (*(__le32 *) (dev->dev_addr + 4)));
+
+- cpw32_f(HiTxRingAddr, 0);
+- cpw32_f(HiTxRingAddr + 4, 0);
+-
+- ring_dma = cp->ring_dma;
+- cpw32_f(RxRingAddr, ring_dma & 0xffffffff);
+- cpw32_f(RxRingAddr + 4, (ring_dma >> 16) >> 16);
+-
+- ring_dma += sizeof(struct cp_desc) * CP_RX_RING_SIZE;
+- cpw32_f(TxRingAddr, ring_dma & 0xffffffff);
+- cpw32_f(TxRingAddr + 4, (ring_dma >> 16) >> 16);
+-
+ cp_start_hw(cp);
+ cpw8(TxThresh, 0x06); /* XXX convert magic num to a constant */
+
+@@ -1192,6 +1219,7 @@ static void cp_tx_timeout(struct net_device *dev)
+ cp_clean_rings(cp);
+ rc = cp_init_rings(cp);
+ cp_start_hw(cp);
++ cp_enable_irq(cp);
+
+ netif_wake_queue(dev);
+
diff --git a/target/linux/generic/patches-3.7/065-8139cp-fixes.patch b/target/linux/generic/patches-3.7/065-8139cp-fixes.patch
new file mode 100644
index 0000000000..33ca1a128a
--- /dev/null
+++ b/target/linux/generic/patches-3.7/065-8139cp-fixes.patch
@@ -0,0 +1,189 @@
+commit 01ffc0a7f1c1801a2354719dedbc32aff45b987d
+Author: David Woodhouse <dwmw2@infradead.org>
+Date: Sat Nov 24 12:11:21 2012 +0000
+
+ 8139cp: re-enable interrupts after tx timeout
+
+ Recovery doesn't work too well if we leave interrupts disabled...
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Acked-by: Francois Romieu <romieu@fr.zoreil.com>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 871f0d4c153e1258d4becf306eca6761bf38b629
+Author: David Woodhouse <dwmw2@infradead.org>
+Date: Thu Nov 22 03:16:58 2012 +0000
+
+ 8139cp: enable bql
+
+ This adds support for byte queue limits on RTL8139C+
+
+ Tested on real hardware.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Acked-By: Dave Täht <dave.taht@bufferbloat.net>
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit a9dbe40fc10cea2efe6e1ff9e03c62dd7579c5ba
+Author: David Woodhouse <dwmw2@infradead.org>
+Date: Wed Nov 21 10:27:19 2012 +0000
+
+ 8139cp: set ring address after enabling C+ mode
+
+ This fixes (for me) a regression introduced by commit b01af457 ("8139cp:
+ set ring address before enabling receiver"). That commit configured the
+ descriptor ring addresses earlier in the initialisation sequence, in
+ order to avoid the possibility of triggering stray DMA before the
+ correct address had been set up.
+
+ Unfortunately, it seems that the hardware will scribble garbage into the
+ TxRingAddr registers when we enable "plus mode" Tx in the CpCmd
+ register. Observed on a Traverse Geos router board.
+
+ To deal with this, while not reintroducing the problem which led to the
+ original commit, we augment cp_start_hw() to write to the CpCmd register
+ *first*, then set the descriptor ring addresses, and then finally to
+ enable Rx and Tx in the original 8139 Cmd register. The datasheet
+ actually indicates that we should enable Tx/Rx in the Cmd register
+ *before* configuring the descriptor addresses, but that would appear to
+ re-introduce the problem that the offending commit b01af457 was trying
+ to solve. And this variant appears to work fine on real hardware.
+
+ Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+ Cc: stable@kernel.org [3.5+]
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 071e3ef4a94a021b16a2912f3885c86f4ff36b49
+Author: David S. Miller <davem@davemloft.net>
+Date: Sun Nov 25 15:52:09 2012 -0500
+
+ Revert "8139cp: revert "set ring address before enabling receiver""
+
+ This reverts commit b26623dab7eeb1e9f5898c7a49458789dd492f20.
+
+ This reverts the revert, in net-next we'll try another scheme
+ to fix this bug using patches from David Woodhouse.
+
+ Signed-off-by: David S. Miller <davem@davemloft.net>
+
+diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c
+index b01f83a..6cb96b4 100644
+--- a/drivers/net/ethernet/realtek/8139cp.c
++++ b/drivers/net/ethernet/realtek/8139cp.c
+@@ -648,6 +648,7 @@ static void cp_tx (struct cp_private *cp)
+ {
+ unsigned tx_head = cp->tx_head;
+ unsigned tx_tail = cp->tx_tail;
++ unsigned bytes_compl = 0, pkts_compl = 0;
+
+ while (tx_tail != tx_head) {
+ struct cp_desc *txd = cp->tx_ring + tx_tail;
+@@ -666,6 +667,9 @@ static void cp_tx (struct cp_private *cp)
+ le32_to_cpu(txd->opts1) & 0xffff,
+ PCI_DMA_TODEVICE);
+
++ bytes_compl += skb->len;
++ pkts_compl++;
++
+ if (status & LastFrag) {
+ if (status & (TxError | TxFIFOUnder)) {
+ netif_dbg(cp, tx_err, cp->dev,
+@@ -697,6 +701,7 @@ static void cp_tx (struct cp_private *cp)
+
+ cp->tx_tail = tx_tail;
+
++ netdev_completed_queue(cp->dev, pkts_compl, bytes_compl);
+ if (TX_BUFFS_AVAIL(cp) > (MAX_SKB_FRAGS + 1))
+ netif_wake_queue(cp->dev);
+ }
+@@ -843,6 +848,8 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
+ wmb();
+ }
+ cp->tx_head = entry;
++
++ netdev_sent_queue(dev, skb->len);
+ netif_dbg(cp, tx_queued, cp->dev, "tx queued, slot %d, skblen %d\n",
+ entry, skb->len);
+ if (TX_BUFFS_AVAIL(cp) <= (MAX_SKB_FRAGS + 1))
+@@ -937,6 +944,8 @@ static void cp_stop_hw (struct cp_private *cp)
+
+ cp->rx_tail = 0;
+ cp->tx_head = cp->tx_tail = 0;
++
++ netdev_reset_queue(cp->dev);
+ }
+
+ static void cp_reset_hw (struct cp_private *cp)
+@@ -957,8 +966,38 @@ static void cp_reset_hw (struct cp_private *cp)
+
+ static inline void cp_start_hw (struct cp_private *cp)
+ {
++ dma_addr_t ring_dma;
++
+ cpw16(CpCmd, cp->cpcmd);
++
++ /*
++ * These (at least TxRingAddr) need to be configured after the
++ * corresponding bits in CpCmd are enabled. Datasheet v1.6 §6.33
++ * (C+ Command Register) recommends that these and more be configured
++ * *after* the [RT]xEnable bits in CpCmd are set. And on some hardware
++ * it's been observed that the TxRingAddr is actually reset to garbage
++ * when C+ mode Tx is enabled in CpCmd.
++ */
++ cpw32_f(HiTxRingAddr, 0);
++ cpw32_f(HiTxRingAddr + 4, 0);
++
++ ring_dma = cp->ring_dma;
++ cpw32_f(RxRingAddr, ring_dma & 0xffffffff);
++ cpw32_f(RxRingAddr + 4, (ring_dma >> 16) >> 16);
++
++ ring_dma += sizeof(struct cp_desc) * CP_RX_RING_SIZE;
++ cpw32_f(TxRingAddr, ring_dma & 0xffffffff);
++ cpw32_f(TxRingAddr + 4, (ring_dma >> 16) >> 16);
++
++ /*
++ * Strictly speaking, the datasheet says this should be enabled
++ * *before* setting the descriptor addresses. But what, then, would
++ * prevent it from doing DMA to random unconfigured addresses?
++ * This variant appears to work fine.
++ */
+ cpw8(Cmd, RxOn | TxOn);
++
++ netdev_reset_queue(cp->dev);
+ }
+
+ static void cp_enable_irq(struct cp_private *cp)
+@@ -969,7 +1008,6 @@ static void cp_enable_irq(struct cp_private *cp)
+ static void cp_init_hw (struct cp_private *cp)
+ {
+ struct net_device *dev = cp->dev;
+- dma_addr_t ring_dma;
+
+ cp_reset_hw(cp);
+
+@@ -992,17 +1030,6 @@ static void cp_init_hw (struct cp_private *cp)
+
+ cpw8(Config5, cpr8(Config5) & PMEStatus);
+
+- cpw32_f(HiTxRingAddr, 0);
+- cpw32_f(HiTxRingAddr + 4, 0);
+-
+- ring_dma = cp->ring_dma;
+- cpw32_f(RxRingAddr, ring_dma & 0xffffffff);
+- cpw32_f(RxRingAddr + 4, (ring_dma >> 16) >> 16);
+-
+- ring_dma += sizeof(struct cp_desc) * CP_RX_RING_SIZE;
+- cpw32_f(TxRingAddr, ring_dma & 0xffffffff);
+- cpw32_f(TxRingAddr + 4, (ring_dma >> 16) >> 16);
+-
+ cpw16(MultiIntr, 0);
+
+ cpw8_f(Cfg9346, Cfg9346_Lock);
+@@ -1192,6 +1219,7 @@ static void cp_tx_timeout(struct net_device *dev)
+ cp_clean_rings(cp);
+ rc = cp_init_rings(cp);
+ cp_start_hw(cp);
++ cp_enable_irq(cp);
+
+ netif_wake_queue(dev);
+