Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
[sfrench/cifs-2.6.git] / drivers / net / fs_enet / mac-fcc.c
index 5d45084b287d13afa6d24dcfc02d18f56afb0ce1..48e91b6242ce99b8382aeb4d55ec17c3af682cc7 100644 (file)
@@ -504,17 +504,54 @@ static int get_regs_len(struct net_device *dev)
 }
 
 /* Some transmit errors cause the transmitter to shut
- * down.  We now issue a restart transmit.  Since the
- * errors close the BD and update the pointers, the restart
- * _should_ pick up without having to reset any of our
- * pointers either.  Also, To workaround 8260 device erratum
- * CPM37, we must disable and then re-enable the transmitter
- * following a Late Collision, Underrun, or Retry Limit error.
+ * down.  We now issue a restart transmit.
+ * Also, to workaround 8260 device erratum CPM37, we must
+ * disable and then re-enable the transmitterfollowing a
+ * Late Collision, Underrun, or Retry Limit error.
+ * In addition, tbptr may point beyond BDs beyond still marked
+ * as ready due to internal pipelining, so we need to look back
+ * through the BDs and adjust tbptr to point to the last BD
+ * marked as ready.  This may result in some buffers being
+ * retransmitted.
  */
 static void tx_restart(struct net_device *dev)
 {
        struct fs_enet_private *fep = netdev_priv(dev);
        fcc_t __iomem *fccp = fep->fcc.fccp;
+       const struct fs_platform_info *fpi = fep->fpi;
+       fcc_enet_t __iomem *ep = fep->fcc.ep;
+       cbd_t __iomem *curr_tbptr;
+       cbd_t __iomem *recheck_bd;
+       cbd_t __iomem *prev_bd;
+       cbd_t __iomem *last_tx_bd;
+
+       last_tx_bd = fep->tx_bd_base + (fpi->tx_ring * sizeof(cbd_t));
+
+       /* get the current bd held in TBPTR  and scan back from this point */
+       recheck_bd = curr_tbptr = (cbd_t __iomem *)
+               ((R32(ep, fen_genfcc.fcc_tbptr) - fep->ring_mem_addr) +
+               fep->ring_base);
+
+       prev_bd = (recheck_bd == fep->tx_bd_base) ? last_tx_bd : recheck_bd - 1;
+
+       /* Move through the bds in reverse, look for the earliest buffer
+        * that is not ready.  Adjust TBPTR to the following buffer */
+       while ((CBDR_SC(prev_bd) & BD_ENET_TX_READY) != 0) {
+               /* Go back one buffer */
+               recheck_bd = prev_bd;
+
+               /* update the previous buffer */
+               prev_bd = (prev_bd == fep->tx_bd_base) ? last_tx_bd : prev_bd - 1;
+
+               /* We should never see all bds marked as ready, check anyway */
+               if (recheck_bd == curr_tbptr)
+                       break;
+       }
+       /* Now update the TBPTR and dirty flag to the current buffer */
+       W32(ep, fen_genfcc.fcc_tbptr,
+               (uint) (((void *)recheck_bd - fep->ring_base) +
+               fep->ring_mem_addr));
+       fep->dirty_tx = recheck_bd;
 
        C32(fccp, fcc_gfmr, FCC_GFMR_ENT);
        udelay(10);